How to fix broken image path when using toStructure()?

Hello

Im new to Kirby/PHP and loving it, but have hit a road block so any help would be greatly appreciated!

I am looking to create a simple gallery page where the client can drag an image from the page files into an image field and also write an image caption.

I have been able to create the blueprint using ‘Structure Field Content’ which outputs correctly in the panel, but the issue is fetching the image in the template. The caption is appearing and the tag is displaying in the browser but the img/path is just displaying the ‘www.website/page’ so no image is loading.

Blueprint:

title: Gallery
pages: false
files: true
options:
  delete: false
fields:
  gallery:
    label: Image
    type: structure
    style: table
    entry: >
      {{gallery_image}}
      {{caption}}
    fields:
      gallery_image:
        label: Image
        type: image
      caption:
        label: Caption
        type: text

Template:

  <section class="gallery">
    <?php foreach($page->gallery()->toStructure() as $pic): ?>
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $pic->image->url() ?>" alt="<?php echo html($pic->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
    <?php endforeach ?>
</section>

There will be multiple images in seperate div’s so not sure if this is even the best approach? Open to a better/cleaner approach!

Thanks in advance!

You’re almost there!

One question before I can provide an answer: what’s $pic->title() ?
Is it a field in your image meta? Is it the name of the image file? I don’t see it in your blueprint.

It was originally calling from the image meta (image.jpg.txt )but not sure if I need it?

At first, I think, it should be <?php echo $pic->image()->url() ?> instead of <?php echo $pic->image->url() ?>.
But then you would try to get some field named image, which does not exist, because your image name is saved in gallery_image.

But to use the url() method, you must have an image object first, wich you can get via the image() function like this:

$img = $page->image($pic->gallery_image());

With this image object, you can generate thumbs, get the url and any other field saved in the image meta file. The caption field will remain the same as it is now.

Just a little correction, it should be

$img = $page->image($pic->gallery_image());

When working with the toStructure() method, you can also get the image like this:

<?php echo $pic->gallery_image()->toFile()->url() ?>

yes of course. I corrected my code.

Alt text and caption can be a bit redundant depending on how you use them. I think the alt text is meant to be very short and descriptive while a caption can provide additional information. There are lots of interesting reads online (I just spent 25 minutes reading through a few) :wink:

Here’s the full code for you. I’m using an if statement to verify that the image exists, to avoid throwing errors.

<section class="gallery">
    <?php foreach($page->gallery()->toStructure() as $pic): ?>
      <?php if($img = $pic->gallery_image->toFile()): ?>
      // If the image exists, create a figure
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <?php endif ?>
    <?php endforeach ?>
</section>

Note 1: You don’t need a structure field if you’re only going to use the images, you could store the caption along the file. See this for reference: https://getkirby.com/docs/panel/blueprints/file-settings#file-fields
You could then use another simpler fields for your needs (I think the gallery field is great for this: Kirby Gallery Field)

Note 2: If you still want to use recipes. There’s a great recipe in the cookbooks that’s all about structure fields. It’s a must read :wink: https://getkirby.com/docs/cookbook/the-structure-field

Thank you everyone for your help! Great community.

The code above works great! But I can’t seem to replicate it for multiple ‘figures’? This is the code…

    <section class="gallery">
    <?php foreach($page->gallery()->toStructure() as $pic): ?>
      <?php if($img = $pic->gallery_image_1->toFile()): ?>
      // If the image exists, create a figure
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <?php endif ?>
    <?php endforeach ?>
    <?php foreach($page->gallery()->toStructure() as $pic): ?>
      <?php if($img = $pic->gallery_image_2->toFile()): ?>
      // If the image exists, create a figure
      <figure class="gallery-item half">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <?php endif ?>
    <?php endforeach ?>
    </section>

Each figure is has a different class for layout purposes.

Im sure im messing up something simple, sorry for novice PHP experience!

What are your trying to do there, where does the $pic->gallery_image_2() come from, can’t see such a field in your structure field?

Sorry — I assumed I would need to create another field in the blueprint for the second image so it could be fetched in the template?

If your images are all in the structure field, you just need to loop through them like in the code above, no need for a second foreach loop. But if you want to have different classes for each field, you would have to define these classes somewhere, e.g. in a field within the structure field or in the image meta file.

Ok so I have removed $pic->gallery_image_2() and looping with foreach. But this returns the same image in every div. How can I display different image in each div?

Could you post your content file please?

No worries.

Title: Gallery

----

Gallery: 

- 
  gallery_image: jt-image-13.jpg
  caption: Image One, Project

So your structure field contains only one image, there is not a lot you can loop through in that case :wink: You would have to add more structure field entries.

But this code:

<section class="gallery">
    <?php foreach($page->gallery()->toStructure() as $pic): ?>
      <?php if($img = $pic->gallery_image()->toFile()): ?>
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <?php endif ?>
    <?php endforeach ?>

should only output one image and not the same image multiple times.

When I add another image and caption in the panel (see below) it repeats the first image in each seperate ‘figure’ then repeats the whole section again for the second image repeating the image in every ‘figure’. This is what is confusing me?!

Content field with multiple images:

Title: Gallery

----

Gallery: 

- 
  gallery_image: jt-image-13.jpg
  caption: Image One, Project
- 
  gallery_image: jt-image-18.jpg
  caption: Image Two, Project

I forgot to add the missing parenthesis after the name of the field, I corrected the code above.

<?php if($img = $pic->gallery_image()->toFile()): ?>

When you call the name of a field, you always have to add them:

$page->title();
$page->text();
// etc.

Thank you again for your help.

I have updated the code but still can’t fetch a different image for the each figure? Not sure what I am missing?

Template code below:

    <?php foreach($page->gallery()->toStructure() as $pic): ?>
      <?php if($img = $pic->gallery_image()->toFile()): ?>
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <figure class="gallery-item half">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <figure class="gallery-item half-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <figure class="gallery-item one fourth">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <figure class="gallery-item half">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <figure class="gallery-item one-fourth">
        <img src="<?php echo $img->url() ?>" alt="<?php echo html($img->title()) ?>">
        <figcaption><?php echo html($pic->caption()) ?></figcaption>
      </figure>
      <?php endif ?>
    <?php endforeach ?>

Thank you in advance for any help!

The problem is that you repeat the same figure/image again and again. The whole purpose of using a foreach loop is that the loop fetches all elements and repeats the same action for each element.

So your code should just consist of this bit of code and nothing else:

<?php foreach($page->gallery()->toStructure() as $pic): ?>
   <?php if($img = $pic->gallery_image()->toFile()): ?>
      <figure class="gallery-item one-third-offset">
        <img src="<?php echo $img->url() ?>" alt="<?php echo $img->title()->html() ?>">
        <figcaption><?php echo $pic->caption()->html() ?></figcaption>
      </figure>
   <?php endif ?>
 <?php endforeach ?>

If you need different classes for each element, I’d suggest that you add these for each image by defining a new field in your blueprint:

title: Gallery
pages: false
files: true
options:
  delete: false
fields:
  gallery:
    label: Image
    type: structure
    style: table
    entry: >
      {{gallery_image}}
      {{caption}}
    fields:
      gallery_image:
        label: Image
        type: image
      class:
        label: Class
        type: text
      caption:
        label: Caption
        type: text

And then modify the above snippet:

<?php foreach($page->gallery()->toStructure() as $pic): ?>
  <?php if($img = $pic->gallery_image()->toFile()): ?>
    <figure class="gallery-item <?php echo $pic->class()->html() ?>">
       <img src="<?php echo $img->url() ?>" alt="<?php echo $img->title()->html() ?>">
       <figcaption><?php echo $pic->caption()->html() ?></figcaption>
    </figure>
  <?php endif ?>
<?php endforeach ?>

Wow that’s perfect!

Adding the class option is the exact solution I didn’t know about! Thank you so much.

So now is it possible to add a selector for the class field in the blueprint? To allow me to add a list of class names for the client to select?