Show Once Per Page, Including Multiple Loops

Is there a way to show a post only once on a page, including multiple loops? I have a projects page, where a project can be “featured” in one loop, but it will automatically appear in the post list. Is there a way to say “if you are being shown in the above loop, do not show here until a new post is added”? Offsetting alone will not work, because the second loop cannot show nested pages.

Here is the first loop:

<?php
    $posts_b = $site->find('portfolio')->children();
    $posts_c = $site->find('portfolio')->grandchildren();
    $posts = new Pages(array($posts_b, $posts_c));
    $posts = $posts->visible()->sortBy('date', 'desc', 'time', 'desc')->paginate(1);
?>

… and the second loop:

<?php
    $posts_b = $site->find('portfolio')->children();
    $posts = new Pages(array($posts_b));
    $posts = $posts->visible()->flip()->paginate(6);
?>

By what sort of criteria do you want to determine if a post should show up in the first/second loop?

The portfolio can show project.groups and project.single templates. A group contains multiple related projects (hence the children), and the single are unrelated projects. The featured area (the first loop) shouldn’t show any of the group pages. Here is a screenshot that might help explain what’s going on:

The “Testing…” project is a Single Template, whereas LB & DFD projects contain children (aka project grandchildren). The Grouped projects should never show as the large image above, but their children are allowed to show there.

Update: I should add that the “Testing…” project is showing twice, when ideally it would show as the large image only and not show in the “Featured Projects” section below until a new project.single page is added. I know I can do this with an added field in the blueprint (i.e. [X] Featured Project), but I was hoping to do something automatic.

Sounds as if the projects use different templates …? If so, you can filter your collections by template:

<?php
    $posts_b = $site->find('portfolio')->children()->filterBy('intendedTemplate', 'project.groups');
    $posts_c = $site->find('portfolio')->grandchildren();
    $posts = new Pages(array($posts_b, $posts_c));
    $posts = $posts->visible()->sortBy('date', 'desc', 'time', 'desc')->paginate(1);
?>

Second loop, no need to create a new Pages object here, you don’t merge anything here.

<?php
    $posts_b = $site->find('portfolio')->children()->filterBy('intendedTemplate', 'project.single')->visible()->flip()->paginate(6);
?>

Edit: I guess you mean limit() instead of paginate()?

Maybe I’m doing something wrong. There are Project Pages (which use the template project.group.php) and Projects (which use the template project.single). I missed this part in my translation of the first loop which is pretty important…

->filterBy('template', '!=', 'project.page')->

I need the project.single template to show in both loops, but if it is already shown in the first loop, it shouldn’t show in the second. An offset won’t work, because a project.group child can alter which posts are shown, as they cannot be shown in the second loop.

Here is the structure, which might help explain things

  • project.single
  • project.group
    • project.single
    • project.single
    • project.single
  • project.group
    • project.single
    • project.single

The first indentation shows in the “Featured Projects” section, whereas all project.single templates should show as large images above.

Switched to limit() now btw :sweat_smile:

Oh, another template? Where is that one used? Or is that a typo?

Anyway, if you don’t have anything by which you can filter, the only option I see is to add all pages from the first loop to a new collection (e.g. $exclude) and exclude that collection from the second loop;

You can use $pages->append() to add each of the elements of the first loop to a new collection:

https://getkirby.com/docs/cheatsheet/pages/append

And then use not($exclude) in your second loop: https://getkirby.com/docs/cheatsheet/pages/not

I think there’s an easier solution here:

First show your featured post

<?php
$featuredpost = page('portfolio')->index()->filterBy('intendedTemplate', 'project.single')->visible()->sortBy('date', 'desc', 'time', 'desc')->first();
?>

What I’m using here that’s different from your code:

  • page() is a Kirby helper equivalent to $site->find()
  • index() recursively creates a full index from all pages and subpages in the collection and returns it as a new flat $pages collection
  • filterBy() is used to select only the pages in the selection that match the filter. Here, the template “project.single”
  • first() returns the first element from the collection

=> This creates a collection with all visible children and grandchildren pages of the portfolio page with the template “project.single”, ordered by date and time (desc).
=> It then picks the first one to be the “$featuredpost”

Then loop through the other posts

// Get all visible first level posts, excludes the fetured post, flips the list, limits to 6 pages
<?php $firstlevelposts = page('portfolio')->children()->visible()->not($featuredpost)->flip()->limit(6);
<?php foreach ($firstlebelposts as $post): ?>
   // Displays all project single pages that are not the featured post
<?php endforeach ?>

Here, as @texnixe suggested, we’re using not() to exclude the $featuredpost from the loop.

Let me know if it works for you !

EDIT: This will not prevent a “group page” that includes the featured “single page” from being displayed in the second loop. If that’s a problem, you need another layer of checks :wink:

This looks like it might do the trick! I won’t be able to test until later in the week, but it definitely looks promising!! I’ll update when I have a chance to play with it. Thanks :slight_smile:

I couldn’t help myself – this works perfectly!! Project Groups are still showing in the second section, and only single projects are showing in the first! Thank you!

For reference, the two bottom-left projects (LoungeBuddy and DFD) are Groups, while the large “banner image above” and Unyielding Perspectives are a Single projects. Before the banner would show twice, but now it only shows once. :clap: :clap: :clap:

Here is how it played out:

<?php
    $featuredpost = page('portfolio')->index()->filterBy('intendedTemplate', 'project.single')->visible()->sortBy('date', 'desc', 'time', 'desc')->first();
    $firstlevelposts = page('portfolio')->children()->visible()->not($featuredpost)->flip()->limit(6);
?>

<?php foreach($firstlevelposts as $post): ?>
1 Like

I’m glad we worked out a solution!
Looking forward to seeing your portfolio finished!

@Thiousi The site is up – thanks for all of your help! You can see the live version of this here: http://jony.io/portfolio :beers:

1 Like

Congrats Jony this is looking great! You should submit it to the showcase. I’ve added your blog to my read later list :slight_smile:

@jony, I’ve had a better look now on my computer and on my verrry slow country house internet (close to 56k, no joke). The images on the homepage for your portfolio appear to be quite large. Have you tried using thumbs to reduce their size and speed up the loading of the homepage?

1 Like

@jony Nice site. Since you are using background images, you could easily use media queries to provide differently sized images for different screen sizes.

Is that a Kirby feature? I’m not a developer, so everything I’ve done is based on different things I’ve read. I know I want to take advantage of the thumbnail feature (https://getkirby.com/docs/templates/thumbnails) in Kirby – is that the same thing?

You can’t use thumbs in your external stylesheets, unless you pre-generate them with Kirby, or use a style tag in your header.

For background images, I’d create different sizes of an image and use media queries to fetch the right one depending on screen resolution, here is an article that explains it.

1 Like