Linking to Virtual Pages from Pages other than their Parent Page

Hi, I’ve set up StarterKit (Kirby 3.7.0.2) and added a Gallery page to it with Virtual Pages

I’ve altered the Photography > Album pages to select/upload images from the Gallery Page and have managed to get the selected child images from the Gallery displayed on the album pages. (Previous post:
Selecting images from another page)

I am now trying to pull through the virtual page URLs for the selected child images to the photography > album pages.

So far I have:

site/controllers/album.php:

<?php
return function ($page) {

    $gallery = $page->content()->get('images')->toFiles()->sortBy('sort', 'filename');

    return [
        'gallery' => $gallery,
    ];

};

site/models/album.php

<?php
class AlbumPage extends Page
{
    public function cover()
    {
        return $this->content()->get('cover')->toFile() ?? $this->image();
    }

	public function children()
	{
		$galleryImages = kirby()->site()->find('gallery')->images()->template('gallery-image');
		$images = [];

		foreach ($galleryImages as $image) {
		  $images[] = [
			'slug'     => $image->name(),
			'num'      => $image->sort()->value(),
			'template' => 'gallery-image',
			'model'    => 'gallery-image',
			'content'  => $image->content()->toArray(),
		  ];
		}

		return Pages::factory($images, $this);
	}
}

site/templates/album.php

<?php snippet('header') ?>
<article>
  <?php snippet('intro') ?>
  <div class="grid">

    <div class="column" style="--columns: 4">
      <div class="text">
        <?= $page->text() ?>
      </div>
    </div>

    <div class="column" style="--columns: 8">
      <ul class="album-gallery">
        <?php foreach ($gallery as $image): ?>
        <li>
          <a href="<?= $image->url() ?>" data-lightbox>
            <figure class="img" style="--w:<?= $image->width() ?>;--h:<?= $image->height() ?>">
              <?= $image->resize(800) ?>
            </figure>
          </a>
        </li>
        <?php endforeach ?>
      </ul>
    </div>

</article>
<?php snippet('footer') ?>

I know I need to update the album template to retrieve the URL, but I’m struggling to get the correct data into the page model first.

This is a print of the $page data on an album page (with two gallery images selected):

AlbumPage Object
(
    [children] => Kirby\Cms\Pages Object
        (
            [0] => photography/trees/blood-moon
            [1] => photography/trees/cheesy-autumn
            [2] => photography/trees/coconut-milkyway
            [3] => photography/trees/dark-forest
            [4] => photography/trees/desert-tree
            [5] => photography/trees/island-from-above
            [6] => photography/trees/last-tree-standing
            [7] => photography/trees/monster-trees-in-the-fog
            [8] => photography/trees/nasty-rocks
            [9] => photography/trees/sharewood-forest
            [10] => photography/trees/stay-in-the-car
            [11] => photography/trees/tent-in-the-woods
        )

    [content] => Kirby\Cms\Content Object
        (
            //Other items removed to save space
            [images] => - gallery/desert-tree.jpg
- gallery/last-tree-standing.jpg
        )
)

I feel like this is almost there, but I’m not sure how to filter the children to only use the urls of the selected images (in this case desert-tree.jpg and last-tree-standing.jpg) AND correct the path. For example, changing from “photography/trees/IMAGE” to “gallery/IMAGE”.

If I print $galleryImages = kirby()->site()->find('gallery')->children()->template('gallery-image') I can then see the list or urls I’d like to filter.

Kirby\Cms\Pages Object
(
    [0] => gallery/blood-moon
    [1] => gallery/cheesy-autumn
    [2] => gallery/coconut-milkyway
    [3] => gallery/dark-forest
    [4] => gallery/desert-tree
    [5] => gallery/island-from-above
    [6] => gallery/last-tree-standing
    [7] => gallery/monster-trees-in-the-fog
    [8] => gallery/nasty-rocks
    [9] => gallery/sharewood-forest
    [10] => gallery/stay-in-the-car
    [11] => gallery/tent-in-the-woods
)

However, if I change $galleryImages in model/album.php to use children() instead of images() like so: $galleryImages = kirby()->site()->find('gallery')->children()->template('gallery-image')

I then only get:

AlbumPage Object
(
    [children] => Kirby\Cms\Pages Object
        (
            [0] => photography/trees/
        )
)

Ideally I’d like to have the $page data on the an album page look like this:

AlbumPage Object
(
    [children] => Kirby\Cms\Pages Object
        (
            [0] => gallery/desert-tree
            [1] => gallery/last-tree-standing
        )

    [content] => Kirby\Cms\Content Object
        (
            //Other items removed to save space
            [images] => - gallery/desert-tree.jpg
- gallery/last-tree-standing.jpg
        )
)

If I can get there, I’m sure I can then update template/album.php to correctly link to the virtual page.

Am I going about this the correct way? Any help would be much appreciated! :slight_smile:

If ive understood what you are trying to do, i did it with a collection

somthing like

<?php
return function ($site) {

    $items = $site->find('gallery')->children()->listed()->filter(function ($child) {
      return $child->files()->count() > 0;
    })->sortBy('num', 'asc');

    return $items;
};

Then you can just call the collection on the page you want to list them out on.

@jimbobrjames Thanks for the suggestion James. I did wonder about using a collection.

It seems that listed() returns a subsection of the virtual page urls, but not actually the ones listed on the current album page.

For example, when using listed() this is returned:

Kirby\Cms\Pages Object
(
    [0] => gallery/blood-moon
    [1] => gallery/coconut-milkyway
    [2] => gallery/dark-forest
    [3] => gallery/desert-tree
    [4] => gallery/tent-in-the-woods
)

I have 12 images in the Gallery, and only 2 are selected on the current album page — only 1 of these appears in the list above.

I’m wondering if I should start from scratch with a different approach?!

I’d like to have a gallery that lists all images, with each of these images having it’s own page (ideally virtual to save having loads of pages with just one image). I’d then like to pick a selection of these to images to appear in album pages, with each image linking back to it’s own page. I’m trying to avoid uploading duplicates.

Maybe virtual pages from the gallery, stored in a collection, and then organised with tags?

There are some parts that I don’t really understand. Why does your album template loop through the images instead of through the defined children?

And this sentence, shouldn’t it be the other way round?

If I understand correctly, you want to filter the image children pages by the selected images? But maybe I’m getting it all wrong…

@texnixe Hi Sonja.

I did try to loop through the children (by setting $galleryImages = kirby()->site()->find(‘gallery’)->children()->template(‘gallery-image’)), however this returned the path to the current album page itself (as below):

AlbumPage Object
(
    [children] => Kirby\Cms\Pages Object
        (
            [0] => photography/trees/
        )
)

Apologies, that section should have read (I missed the extensions):

I feel like this is almost there, but I’m not sure how to filter the children to only use the urls of the selected images (in this case desert-tree.jpg and last-tree-standing.jpg) AND correct the path. For example, changing from “photography/trees/IMAGE” to “gallery/IMAGE”.

Yes I think that’s what I’m trying to do :joy:

If my album page photography/trees/ has currently selected desert-tree.jpg and last-tree-standing.jpg from the Gallery page, I’d like to return the URLs of the corresponding virtual pages.

This should work (if I haven’t introduced any errors)

$filtered = $page-children()->filter(fn($child) => in_array($child->slug(), $page->content()->get('images')->toFiles()->pluck('name', ',')));

What this does:

  • it fetches all virtual children with $page->children()
  • then filters them with a callback
  • in that callback, I check if the slug of the current element is in the array of file names plucked from the images field, so page->content()->get('images')->toFiles()->pluck('name', ',') will give you an array
['desert-tree', 'last-tree-standing']

@texnixe Thanks Sonja! I think I’ve got it sussed now! I really appreciate your help with this :smiley:

Here’s what I’ve done for anyone else that needs to do something similar.

If I use this directly on the album.php page, the result is:

Kirby\Cms\Pages Object
(
   [0] => photography/trees/desert-tree
   [1] => photography/trees/last-tree-standing
)

However, if I change it to:

$filtered = $site->find('gallery')->children()->filter(fn($child) => in_array($child->slug(), $page->content()->get('images')->toFiles()->pluck('name', ',')));

Then the result is (which is what I was looking for):

Kirby\Cms\Pages Object
(
    [0] => gallery/desert-tree
    [1] => gallery/last-tree-standing
)

I’ve updated controller/album.php to this:

<?php
return function ($page,$site) {

    $galleryFiltered =  $site->find('gallery')->children()->filter(fn($child) => in_array($child->slug(), $page->content()->get('images')->toFiles()->pluck('name', ',')));

    return [
        'galleryFiltered' => $galleryFiltered,
    ];

};

The corresponding section of the templates/album.php has been updated to:

<ul class="album-gallery">
	<?php foreach ($galleryFiltered  as $child): ?>
	<li>
		<a href="<?= $child->url() ?>">
		<?php if ($image = $child->image()) : ?>
			<figure class="img" style="--w:<?= $image->width() ?>;--h:<?= $image->height() ?>">
			<?= $image->resize(800) ?>
			</figure>
		<?php endif; ?>
		</a>
	</li>
	<?php endforeach ?>
</ul>

I couldn’t work out how to do it via the models/album.php so it has been changed back to the default from StarterKit.

Not sure if the way I’ve done it is the best way, but it seems to be working now! :man_shrugging:

Have tested with a few more albums, and the selected images from Gallery are appearing and the virtual pages they link to are also working.

Thanks again! :slight_smile: