How to get every field from inside a structure field?


The general question:
What is the best way to get every field as a field object from inside a structure field?

The more specific question:
I have a page with a structure field that can contain blocks. I want to get the first image block that is stored somewhere in there. How do I do that?

If I do something like that

    $structureArray = $route->steps()->toStructure()->toArray();
    $blocks = array_map(function($item) {
      return $item['blocks'];
    }, $structureArray);

I only get the blocks in String form as the toArray() function converts them I suppose. I could ofc search for the first that contains a “src” and extract it, but it feels wrong.

Could you post your blueprint definition of the structure field, please?

Sure. Sorry, should’ve included that from the start.

    type: fields
        label: Steps
        type: structure
        min: 1
            label: Blocks
            type: blocks
              - image

So inside your structure you only have a blocks field which can only have image blocks, and you want to collect the first image block from all items in the structure into a single collection? Do I understand this correctly?

Yes! Ultimately I would just like to get the url of the image so I can have it as a preview-image. Which would be possible with the string, but I should be able to get the block object too, right?

// create a new Blocks instance
$blocks = new Kirby\Cms\Blocks();
// get an array of blocks fields from all structure items of the current page
$blockFields = $page->steps()->toStructure()->pluck('blocks');
// loop through these fields...
foreach ($blockFields as $field) {
  // and convert each field to blocks, filter by type and add the first one to the Blocks instance
  $blocks->add($field->toBlocks()->filterBy('type', 'image')->first());

// now that we have the blocks we are interested in, we can pluck the image fields

$imageFields = $blocks->pluck('image');
$urls = [];
foreach ($imageFields as $imageField) {
  $urls[] = $imageField->toFile()->url();

Disclaimer: Not tested. And maybe there is a shorter way.

1 Like

Thank you texnixe! That worked!

I might have been unclear, but I actually only need the first image, so this much shorter version also did the trick:

$blockField = $route->steps()->toStructure()->pluck('blocks')[0]->blocks()->toBlocks()->first();
$previewImageURL = $blockField->toFile()->url();