Modules plugin: Print all modules from all visible pages

One more, how do I print all modules from all visibile pages?

I tried the following with no good results

<?php foreach($pages->visible() as $canvas): ?>
  <p class="ta--c c--wh"><?= $canvas->title() ?></p>
    
  <main role="main">
    <?= $canvas->modules() ?>
  </main>
<?php endforeach ?>

What I get is half the content from $canvas->modules() but all the $canvas->title().

Using <?= $pages->modules() ?>prints nothing.

Any way to accomplish this?

According to the docs:

 <?php $canvas->modules() ?>

without the echo

Yes I tried that as well, but it gives a partial output as described above.

What do you mean by partial output? Maybe some module snippets are missing or not named correctly? Or the data is missing? Hard to tell from remote…

My bad indeed, if i use

  <?php foreach($site->index() as $canvas): ?>

it works, but i get all the pages displayed twice. Should I maybe make a custom page collection to avoid this?

$site->index() fetches all pages, including modules, don’t think that’s what you want. I don’t know your page structure and I don’t know what exactly you are trying to do, so it’s a bit difficult. Maybe you can provide some more information about your page structure and what you want to output ?

A custom collection or limiting the depth and visibility might be helpful.

Yes, the content structure is as follows:

content/
  page/
    modules/
      modules.txt
    default.txt

  page/
    modules/
      modules.txt
    default.txt
    subpage/
      modules/
        modules.txt
      default.txt

  home
    default.txt
    modules/
      modules.txt

So I need to check for modules both in depth(1) and depth(2) and include the home page to the collection.

Either I go with two nested foreach loopsand an if or I manually merge two collections into one?

Maybe you can do something like this:

$collection = $site->index()->filterBy('depth', '<=', 2)->filterBy('template', 'not in', [array, of, module, template];

Very elegant solution, thank you!

Last thing: even though I put the modules template in the collection

<?php $collection = $site->index()->filterBy('depth', '<=', 2)->filterBy('template', 'not in', ['headline', 'text', 'image']) ?>

I still have some empty looped objects, in example the empty div wrapping each module.

<?php foreach($collection->visible() as $canvas): ?>
  <div class="canvas">
    <?= $canvas->modules() ?>
  </div>
<?php endforeach ?>

I thought it would go away by adding the filterBy('template') rule, but if I take it away the empty divs are still shown.

Full template code

  <main role="main">
    <?php $collection = $site->index()->filterBy('depth', '<=', 2)->filterBy('template', 'not in', ['modules', 'headline', 'text', 'image']) ?>

    <?php foreach($collection->visible() as $canvas): ?>
      <div class="canvas">
        <?= $canvas->modules() ?>
      </div>
    <?php endforeach ?>
  </main>

Why are you filtering the collection again? If you want to filter by visible, it would be more performant to to filter before you filter by anything else.

Maybe you should do some dumps in the right places to see if the output is what you expect it to be in the first place.

Thanks for the performance suggestion.

I’ll try to go the var_dump way, though before printing all pages and subpages together, I had each page on its own and the output was correct.

Thanks!

I finally realised what makes those empty divs, it’s the parent page of a subpage. When there are subpages, I don’t want to output anything from its parent page, so the logic would go along the lines of:

→ if page has subpage, take only subpage, else take page

I think for this there needs to be some if/else code, but am thinking how magic it would be to filter it out already from the root $collection altogether.

You can filter by basically any page method that makes sense, so also by hasChildren(). So try

<?php $collection = $site->index()->visible()->filterBy('depth', '<=', 2)->filterBy('template', 'not in', ['modules', 'headline', 'text', 'image'])->filterBy('hasChildren') ?>

but check if that gives you the desired result with dump().

Adding the hasChildren filter was not helping unfortunately.

For now I have something like this, that’s working good, but maybe not such good code performance and intelligence wise

    <?php $collection = $site->index()->visible()->filterBy('depth', '<=', 2) ?>
    
    <?php foreach($collection as $canvas): ?>
      
      <?php if($canvas->hasVisibleChildren()): ?>
        <?php foreach($canvas->children()->visible() as $c): ?>
          <div id="<?= $c->title()->lower()->html() ?>" class="w--full">
            <?= $c->modules() ?>
          </div>
        <?php endforeach ?>
      <?php elseif($canvas->depth() == 1): ?>
        <div id="<?= $canvas->title()->lower()->html() ?>" class="w--full">
          <?= $canvas->modules() ?>
        </div>
      <?php endif ?>

    <?php endforeach ?>

Look’s like you are only using the $collection variable once, so you can loose that line and do this instead:

Is it essential to use a lower case page title as an ID? I normally use uid() for this, so you can shorten that a little. Will give you the page directory name.

<?php foreach($site->index()->visible()->filterBy('depth', '<=', 2) as $canvas): ?>
  
  <?php if($canvas->hasVisibleChildren()): ?>
    <?php foreach($canvas->children()->visible() as $c): ?>
      <div id="<?= $c->uid() ?>" class="w--full">
        <?= $c->modules() ?>
      </div>
    <?php endforeach ?>
  <?php elseif($canvas->depth() == 1): ?>
    <div id="<?= $canvas->uid() ?>" class="w--full">
      <?= $canvas->modules() ?>
    </div>
  <?php endif ?>

<?php endforeach ?>

It makes sense to store the collection in a variable, even if you don’t reuse it. You can put that bit into a controller if you want or at the top of the template, so you can change it more easily later if required (rather than looking for the collection further down in your template. Also there might be a reason for using the title instead of the UID.

Sure, it’s just @afincato was looking to reduce the code and improve performance, i figured that $c->uid is probably much faster for Kirby to figure out then $c->title()->lower()->html(). If all that is needed is some kind of hook for CSS or JavaScript, it works great.

Yes, I usually also use the UID, because Kirby doesn’t have to read the file, but title or UID, both may fall short if the user is allowed to change them.

My main point, however, was that I wouldn’t recommend dumping the variable prior to looping through the collection.

Thanks both for the suggestions!

Using ->uid() is indeed much better, I forgot about it. I usually tell clients to update also the url link if they change the title after they created the page. But as you point out, it’s a possible problem if they don’t…