Stuck with multiple loops, controller and Blade directives

This is my trickiest coding so far in my self learning journey and I’m struggling to understand the best way forwards.

I have a CPT called programme and custom fields which give dates and details for events. I’m trying to display events in tabs based on date. Each tab has 6 months of events and they’re fixed as Jan-Jun and Jul-Dec, not a rolling 6 months.

To create the tabs I did this

# app/Controllers/TemplateProgramme.php

class TemplateProgramme extends Controller
{
    public function tabs() {
        $y = date('Y');
        $jj = 'Jan-Jun ';
        $jd = 'Jul-Dec ';
        $sjj = '01 Jan ';
        $ejj = '30 Jun ';
        $sjd = '01 Jul ';
        $ejd = '31 Dec ';

        // are we in the first or second half of the year?
        if(date('m') <= 6) { // it's currently Jun ar earlier, so we start with the Jan-Jun Programme.
            return [
                [
                    'key'   => 'tab1',
                    'title' => $jj . $y,
                    'start' => $sjj . $y,
                    'end'   => $ejj . $y,
                ],
                [
                    'key'   => 'tab2',
                    'title' => $jd . ($y - 1),
                    'start' => $sjd . ($y - 1),
                    'end'   => $ejd . ($y - 1),
                ],
                [
                    'key'   => 'tab3',
                    'title' => $jj . ($y - 1),
                    'start' => $sjj . ($y - 1),
                    'end'   => $ejj . ($y - 1),
                ],
                [
                    'key'   => 'tab4',
                    'title' => $jd . ($y - 2),
                    'start' => $sjd . ($y - 2),
                    'end'   => $ejd . ($y - 2),
                ],
                [
                    'key'   => 'tab5',
                    'title' => $jj . ($y - 2),
                    'start' => $sjj . ($y - 2),
                    'end'   => $ejj . ($y - 2),
                ],
            ];
        } else { //it's currently July or later, so we start with the Jul-Dec Programme
            return [
                [
                    'key'   => 'tab1',
                    'title' => $jd . $y,
                    'start' => $sjd . $y,
                    'end'   => $ejd . $y,
                ],
                [
                    'key'   => 'tab2',
                    'title' => $jj . $y,
                    'start' => $sjj . $y,
                    'end'   => $ejj . $y,
                ],
                [
                    'key'   => 'tab3',
                    'title' => $jd . ($y - 1),
                    'start' => $sjd . ($y - 1),
                    'end'   => $ejd . ($y - 1),
                ],
                [
                    'key'   => 'tab4',
                    'title' => $jj . ($y - 1),
                    'start' => $sjj . ($y - 1),
                    'end'   => $ejj . ($y - 1),
                ],
                [
                    'key'   => 'tab5',
                    'title' => $jd . ($y - 2),
                    'start' => $sjd . ($y - 2),
                    'end'   => $ejd . ($y - 2),
                ],
                [
                    'key'   => 'tab6',
                    'title' => $jj . ($y - 2),
                    'start' => $sjj . ($y - 2),
                    'end'   => $ejj . ($y - 2),
                ],
            ];
        };
    }
}
# resources/views/template-programme.blade.php

<!-- set up the tabs -->
    <ul class="nav nav-pills" id="tabs">
      @foreach ($tabs as $tab)
      <li class="nav-item">
        <a 
          class="nav-link"
          id="{{ $tab['key'] }}"
          data-toggle="tab"
          href="#{{ $tab['key'] }}"
          role="tab"
          aria-controls="{{ $tab['key'] }}-content"
          aria-selected="false"
        >
          {{ $tab['title'] }}
        </a>  
      </li>
      @endforeach
    </ul>
  <!-- end of tabs -->

Which works but feels a bit clunky. However when I come to create the tab panels the loop isn’t working.

<div class="tab-content" id="tab-content">
  @foreach ($tabs as $tab)
    <div class="tab-pane fade" id="{{ $tab['key'] }}-content" role="tabpanel" aria-labelledby="{{ $tab['key'] }}">
      <!-- setup the query for Programme posts -->
      @query([
        'posts_per_page'	=> -1,
        'post_type'	        => 'programme',
        'meta_query' 	  	=> [
          [
            'key'		      	=> 'field_prog_start_date',
            'compare'	    	=> 'BETWEEN',
            'value'		    	=> [
              $tab['start'],
              $tab['end'],
            ],
            'type'		     	=> 'DATETIME'
          ],
        ],
        'order'				 => 'ASC',
        'orderby'		     => 'meta_value',
        'meta_key'	  		 => 'field_prog_start_date',
        'meta_type'		  	 => 'DATETIME'
      ])
      <!-- Now we set up the content for each panel -->
      <table class="table">
          <thead>
            <tr>
              <th scope="col">Date</th>
              <th scope="col">Title</th>
              <th scope="col">Description</th>
            </tr>
          </thead>
          <tbody>
            
            <!-- start loop -->
            @posts
            <tr class="table-primary">
              <th scope="row">@field('field_event_start_date')</th>
              <td><a href="<?php the_permalink() ?>">@title</a></td>
              <td>@field('field_event_description')</td>
            </tr>
            <!-- end loop -->
            @endposts
          </tbody>
        </table>
        <!-- end table -->
    </div>
  @endforeach
</div>
<!-- the table -->

The Bootstrap pills display, and the divs are created for the panes, but no table is displayed.

I think where I’m going wrong is with the query within a foreach loop. I read through @alwaysblank’s answer about multiple loops but I don’t understand it.

So, is running multiple queries to get the posts by date the right way to do this? If so, how do I go about running the queries in controller based on variables in a previous method? Pointers, links to articles, and criticism all welcome and very much appreciated…

Personally I would definitely shy away from running a query in your blade: That kind of stuff should IMO be done in a controller. Running a query for each tab also seems like you’re hitting the DB way more often than you need to.

In situations similar to this, my approach is usually the following in the controller:

  1. Write a query that gets all the posts that I will be showing, all at once.
  2. Iterate over the posts in that query and sort/filter/whatever them out into some other data structure, which is then passed to the blade. The exactly logic for this sorting/filtering/whatevering would be up to you: I admit I don’t fully understand your data organization.

In this case, I would probably try and build an array that looks a little like this:

[
  'section1' => [
    'tab' => ['title' => "tab 1 title", 'key' => "tab1key"],
    'posts' => [ /** all the posts that should appear under this tab **/ ],
  ],
  'section2' => [
    'tab' => ['title' => "tab 2 title", 'key' => "tab2key"],
    'posts' => [ /** all the posts that should appear under this tab **/ ],
  ],
  /** etc... **/
];

Then you can iterate over them like this (very simplified):

<ul class="tabs">
  @foreach($sections as $section)
    <li><a  href="#{{ $section['tab']['key'] }}">{{ $section['tab']['key'] }}</a></li>
  @endforeach
</ul>
<div class="posts">
  @foreach($sections as $section)
    <div id="{{ $section['tab']['key'] }}">
      @foreach($section['posts'] as $post)
        <h1>{{ $post->post_title }}</h1>
      @endforeach
    </div>
  @endforeach 
</div>

This makes your blade more readable, and it makes it clearer what data you’re using and what you’re doing with it.

Ah ha, thanks Ben! that makes sense and seems much more eloquent!

1 Like

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