Page model for virtual children pages on the fly

Hello,

While using MERX, in order to return product variants on the fly, I am testing this idea which uses virtual pages:

The idea is to create a page model for the product page, in order to override the children() method, so it returns product variants as a collection of virtual children pages.

In my case, tho, there are some differences.

  1. The blueprint I want to create a model for is called fanware-product.yml and I don’t have a fanware-product.php template because I don’t visit this page at any time.
  2. In this case what should drive the creation of virtual children is a sizes() structure field.

So my first attempt for the model is this:

/site/models/fanwareproduct.php

<?php
class FanwareProductPage extends Kirby\Cms\Page
{
    public function children()
    {
        $sizedVariants = [];

        foreach ($this->sizes()->toStructure() as $size) {
            $sizedVariants[] = [
                'slug'     => $this->slug() . $size->size(),
                'template' => 'product-variant',
                'model'    => 'product-variant',
                'content'  => [
                    'title'  => $this->title() . '(' . $size->size() . ')',
                    'price'  => $this->price()->toFloat(),
                ]
            ];
        }

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

I test this in the homepage (not in the fanware product page) with the following code :

// $fanwareProducts only has 1 page in it

<?php foreach($fanwareProducts->shuffle() as $fanwareProduct): ?>
<?php dump($fanwareProduct->sizes()) ?>
<?php dump($fanwareProduct->children()) ?>
<?php endforeach ?>

Which results in:

Kirby\Cms\Field Object
(
    [sizes]  => - 
  size: S
  outofstock: 'false'
  default: 'true'
- 
  size: M
  outofstock: 'false'
  default: 'false'
- 
  size: L
  outofstock: 'true'
  default: 'false'
- 
  size: XL
  outofstock: 'false'
  default: 'false'
)

Kirby\Cms\Pages Object
(
)

…so I am getting an empty children collection

Is there anything specifically wrong about this model or how I am using it ?

Thanks

If the content file name is fanware-product.txt the filename of the model should also be fanware-product.php. Not sure if that is the issue because you only mention the non existing

That was it, now it works, thank you.

@texnixe for this case when creating this virtual children, ideally they would be clones of the real parent page with the exception of a different title. Is there an easy way to achieve this ?

For example, in templates, I will need to get the image for the virtual child, which should be the same image in the real parent.

Thanks

The you need a model for the children where you define what these elements are supposed to be, i.e. redefine the image etc.

You can see that I do something similar in this yet unpublished recipe, line 200:

Thank you,

So, considering that when I do virtualchild->image() I want to get the first image of the parent, I could do, in the virtual child model, something like:

class productVariant extends Page
{
  public function image()
  {
    return parent::image();
    // OR  
    return $this->parent()->image()
  }
}

Could I actually define the image when defining the virtual child in the parent’s model ?

class FanwareProductPage extends Kirby\Cms\Page
{
    public function children()
    {
        $sizedVariants = [];

        foreach ($this->sizes()->toStructure() as $size) {
            $sizedVariants[] = [
                'slug'     => $this->slug() . $size->size(),
                'template' => 'product-variant',
                'model'    => 'product-variant',
                'content'  => [
                    'title'  => $this->title() . '(' . $size->size() . ')',
                    'price'  => $this->price()->toFloat(),
                    // Define here the image somehow as the parent's image ?
                ]
            ];
        }

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

Thanks

That would return the image from the parent page, which is probably what you want.

Not within the content array, but within the files property

Where can I find documentation or examples on how to fill the files property ? I am a bit lost on where to start … should I dump the parent and mimick the structure of files property ?

Danke

It seems that providing the parent’s files as array, works, which is pretty cool and powerful:

    ...
    $sizedVariants[] = [
        'slug'     => $this->slug() . $size->size(),
        'template' => 'product-variant',
        'model'    => 'product-variant',
        'files' => $this->files()->toArray(),
        'content'  => [
            'title'  => $this->title() . '(' . $size->size() . ')',
            'price'  => $this->price()->toFloat()
        ]
    ];
    ...

Hi there again @texnixe

Giving the virtual children the files from the real parent seem to work at first:

        foreach ($this->sizes()->toStructure() as $size) {
            $sizedVariants[] = [
                'slug' => $size->size(),
                'num' => $this->num(),
                'files' => $this->files()->toArray(),
                'content'  => [
                    'title'  => $this->title() . ' (' . $size->size() . ')',
                    'size' => $size->size()->toString()
                ]
            ];
        return Pages::factory($sizedVariants, $this);

Even calling ->image()->url() on those virtual children, give me a valid functioning image url.

BUT when doing a resize such as :

<img src="<?= $virtualChild->image()->resize(1)->url() ?>" >

I am getting a broken image icon in place of the image.

The only difference between the working and non working image urls is that the first seems to belong to the real parent’s media folder, while the second seems to belong to the virtual child media folder:

This works:

http://localhost/~jaume/dev/dietz/media/pages/offline-shopping-bag/1477797723-1615073377/dietz_julia-esque_real-life-shopping-bag.jpg

This does not (the virtual child id is simply ‘S’):

http://localhost/~jaume/dev/dietz/media/pages/offline-shopping-bag/S/1477797723-0/dietz-julia-esque-real-life-shopping-bag-1x.jpg

Did I stumble upon something arcane here?

danke

It will work if you add the model for the product variants with the image/files methods:

   public function image(?string $filename = null)
    {
        return $this->parent()->image($filename);
    }

  public function files() {
      return $this->parent()->files();
    }

Maybe also images() if you use that method.

Otherwise the problem seems to be that Kirby doesn’t know if the thumbs should be created or not, because the second part of the media hash (which is the hashed file modification date) is zero. So while the jobs get created in the media folder, the thumbs do not.

1 Like

Thank you