Filtering three loops for a blog


#1

Hi everyone,

I’m a noob… I’m putting together a blog style news section for a client. I can’t work out how to create three loops that are each filtered by a specific tag. There seems to be issues when making subpages invisible and/or using the ‘past-news’ tag to filter. Any advice would be awesome! Thanks.

<h2>Current</h2>

    <?php if($articles->count()): ?>

    <?php $articles = $page->children()
               ->visible()
               ->filterBy('tags', 'current-news', ',') ; ?>

      <?php foreach($articles as $article): ?>



      <article>
        <div class="image-container" style="background-image:url('<?php if($image = $article->image()): ?>
        <?php echo $article->image()->url() ?>
        <?php endif ?>');"></div>
        <div class="container">
          <a href="<?= $article->url() ?>"><h1 class="headline"><?= $article->title()->html() ?></h1></a>
          <time><?= $article->date('d/m/Y') ?></time>
          <p><?= $article->text()->kirbytext()->excerpt(20, 'words') ?> </p>
          <a class="read-more" href="<?= $article->url() ?>">Read more</a>
        </div>
      </article>


  <?php endforeach ?>
<?php else: ?>
  <p>This blog does not contain any articles yet.</p>
<?php endif ?>





  <h2>Future</h2>

  <?php if($articles->count()): ?>

  <?php $articles = $page->children()
             ->visible()
             ->filterBy('tags', 'future-news', ','); ?>

    <?php foreach($articles as $article): ?>

      <article>
        <div class="image-container" style="background-image:url('<?php if($image = $article->image()): ?>
        <?php echo $article->image()->url() ?>
        <?php endif ?>');"></div>
        <div class="container">
          <a href="<?= $article->url() ?>"><h1 class="headline"><?= $article->title()->html() ?></h1></a>
          <time><?= $article->date('d/m/Y') ?></time>
          <p>National sports charity ‘Tennis For Free’ has served up an ace for Watford by investing in a year-long programme of free tennis sessions. </p>
          <a class="read-more" href="<?= $article->url() ?>">Read more</a>
        </div>
      </article>


    <?php endforeach ?>
  <?php else: ?>
    <p>This blog does not contain any articles yet.</p>
  <?php endif ?>



  <h2>Past</h2>

  <?php if($articles->count()): ?>

  <?php $articles = $page->children()
             ->visible()
             ->filterBy('tags', 'past-news', ','); ?>

    <?php foreach($articles as $article): ?>

      <article>
        <div class="image-container" style="background-image:url('<?php if($image = $article->image()): ?>
        <?php echo $article->image()->url() ?>
        <?php endif ?>');"></div>
        <div class="container">
          <a href="<?= $article->url() ?>"><h1 class="headline"><?= $article->title()->html() ?></h1></a>
          <time><?= $article->date('d/m/Y') ?></time>
          <p>National sports charity ‘Tennis For Free’ has served up an ace for Watford by investing in a year-long programme of free tennis sessions. </p>
          <a class="read-more" href="<?= $article->url() ?>">Read more</a>
        </div>
      </article>


    <?php endforeach ?>
  <?php else: ?>
    <p>This blog does not contain any articles yet.</p>
  <?php endif ?>

#2

Your problem is a little unclear - does it break if you pages invisible? Does current-news work but past-news does not?

You have an awful lot of repeated code. I would move the loop into a snippet and pass the tag you want to filter on as a parameter. That way you can shorten that code by 70%.

I think the problem you have is that you are checking to see if $articles has a count before you have set the $articles variable with a value, so it’s probably always going to come up with nothing.

Swap your count and variable lines round:

<h2>Current</h2>
    <?php $articles = $page->children()
           ->visible()
           ->filterBy('tags', 'current-news', ',') ; ?>

    <?php if($articles->count()): ?>
      <?php foreach($articles as $article): ?>
      <article>
        <div class="image-container" style="background-image:url('<?php if($image = $article->image()): ?>
        <?php echo $article->image()->url() ?>
        <?php endif ?>');"></div>
        <div class="container">
          <a href="<?= $article->url() ?>"><h1 class="headline"><?= $article->title()->html() ?></h1></a>
          <time><?= $article->date('d/m/Y') ?></time>
          <p><?= $article->text()->kirbytext()->excerpt(20, 'words') ?> </p>
          <a class="read-more" href="<?= $article->url() ?>">Read more</a>
        </div>
      </article>
  <?php endforeach ?>
    <?php else: ?>
  <p>This blog does not contain any articles yet.</p>
<?php endif ?>

Also i am not sold that using count to check for the existence of articles is enough - I think you need to check the count is greater than 0 since count will always return a number, even if its zero, which means your statement will always be true.

<?php if($articles->count() > 0): ?>

To do it as a snippet, move the whole loop into a snippet file called bloglist.php described in the link above and pass variables thought to it (untested but it should work):

<h2><?= $title ?></h2>
    <?php $articles = $page->children()
           ->visible()
           ->filterBy('tags', $tagfilter, ',') ; ?>

<?php if($articles->count() > 0): ?>

      <?php foreach($articles as $article): ?>
      <article>
        <div class="image-container" style="background-image:url('<?php if($image = $article->image()): ?>
        <?= $article->image()->url() ?>
        <?php endif ?>');"></div>
        <div class="container">
          <a href="<?= $article->url() ?>"><h1 class="headline"><?= $article->title()->html() ?></h1></a>
          <time><?= $article->date('d/m/Y') ?></time>
          <p><?= $article->text()->kirbytext()->excerpt(20, 'words') ?> </p>
          <a class="read-more" href="<?= $article->url() ?>">Read more</a>
        </div>
      </article>
    <?php endforeach ?>
      <?php else: ?>
      <p>This blog does not contain any articles yet.</p>
<?php endif ?>

That way you can just call it like this, reducing the code for it in your template to just 3 lines:

<?php snippet('bloglist', ['title' => 'Current', 'tagfilter' => 'current-news']) ?>
<?php snippet('bloglist', ['title' => 'Future News', 'tagfilter' => 'future-news']) ?>
<?php snippet('bloglist', ['title' => 'Past News', 'tagfilter' => 'past-news']) ?>

However, personally I would use a date field on the article to determine wether its past, present, of future news, rather than tags. It’s even less code, and easier to manage because you won’t have to remember to manually re-tag stuff once its old news :slight_smile:


#3

Thanks so much for your reply. It’s really detailed and clear, I appreciate it. I’ll try to be clearer in my next post!

Swapping count and variables around did work, thanks for that.

The snippet idea is definitely the way to go. I’ll try to implement it. I agree on the more logical approach to the posts but the client wanted the ability to move them between categories easily. (I may contact you if that changes :slightly_smiling_face:

Thanks again!


#4

Just to answer this: no, you don’t need to check for greater 0, because 0 returns false in PHP

https://secure.php.net/manual/en/language.types.boolean.php