KQL get full image path for files field in block?

Hey,

I’m currently using KQL to return an array of blocks from the blocks field. I have a custom block inside this which contains a files field to output an image.

I’m currently returning blocks like so:

{
					query: "page('home')",
					select: {
						url: "page.url",
						uri: "page.uri",
						title: "page.title",
						blocks: "page.blocks.toBlocks",
					}
				}

but this only returns the filename, has anyone found a way of returning the full path within the toBlocks method, or is it possible to overwrite this?

Maybe this helps?

1 Like

I looked at this thread before posting, I think this differs slightly as that is overwriting the image field type. I’m looking at accessing a files field within a custom block it’s literally text and and image field so relatively simple.

The issue arises when i try to access the image by prefixing what I think is the url for example if I wanted an image from the homepage i would expect it to be at https://mydomain.com/home/image.png but it should come from the media folder from a url like https://mydomain.com/media/pages/home/85e9e1d24e-1629881942/image.png

Using KQL in the above way to output blocks content, i cant see how i could get that URL for the custom block’s files field.

Maybe one for @texnixe?

I have no experience whatsoever with KQL. But I think you either have to make your custom blocks return the correct image urls from the media folder in the first place, or you deliver your images from the content folder instead of from the media folder. For this to work, you would have to change the .htaccess to allow accessing images from the media folder (blocked by default) and create a file::url component that does the job, for this see Protecting files behind a firewall | Kirby CMS

1 Like

How could i ensure my custom block delivers the correct url from the files field though? would this be in a hacky way like a hidden field updated on a hook? or is there another way i could change the output without messing with the core?

The custom block I’m referring too is just a blueprint with a few standard fields, there is zero output on the kirby front end - everything is being accessed by KQL from another server.

Appreciate the help!

But what I don’t really understand is why the image URLs are not resolved when you call toBlocks() and pass that to KQL. Like the same way it is resolved when you call toBlocks() in a template. Excuse my ignorance, as I said, never used KQL.

1 Like

This is what is returned, wondering if its easier to just use content representations instead of KQL. It works brilliant for every other bit of data I need but only on blocks it seems a bit restrictive.

I can query the images of the page fine when they arent in blocks and get them into the front-end but would have to then match them based on the filename in the front-end which seems a bit hacky.

"code":200,
"result":{
"title":"Home",
"blocks":[
   {
   ...
      "content":{
          "title":"this is a title",
          "text":"this is some text",
          "image":[
                "image.jpg"
           ],
       }
   },
   ...
],

in an ideal world, what i’d like to output would be the following:

"code":200,
"result":{
"title":"Home",
"blocks":[
   {
   ...
      "content":{
          "title":"this is a title",
          "text":"this is some text",
          "image":[
                "https://mydomian.com/media/pages/somepage/image.jpg"
           ],
       }
   },
   ...
],

or for it to return the file object rather than just a filename.

I see, that is indeed less than ideal. Yes, maybe content representations would be better suited.

I have no idea what you are supposed to do with the information that KQL returns in this case, unless there is a way to tweak the output somehow.

1 Like

If you want to keep using KQL, just add a field method that does the work for you on the server side.

{
    query: "page('home')",
    select: {
      url: "page.url",
      uri: "page.uri",
      title: "page.title",
      blocks: "page.blocks.myFieldMethod",
    }
}

The field method would then just call toBlocks, convert the result to an array, do the necessary edits and then return the array. I’d expect that to work.

It’s essentially the same work you would do in the content representation, but it’s accessible from KQL, and it could even allow you to parametrize the thing. Like, as an example, myFieldMethod("xs") could not only transform the blocks image names to files, but could also create thumbnails for them…

3 Likes

@rasteiner, That sounds like a good shout, I can find the image filename in the array but guess i’d have to find it by the blueprint key, meaning any blueprints where I add an image field, these would have to be called by name, right?

since the field only outputs an array of image filenames, there isnt any other way of searching? unless you could search purely for an array with certain content?

Thanks

Maybe something like this?


use Kirby\Cms\App as Kirby;

Kirby::plugin('minml/myFieldMethod', [
    'fieldMethods' => [
        'myFieldMethod' => function($field) {
            $model = $field->parent();
            $blocks = $field->toBlocks()->toArray();

            if (class_uses($model, 'Kirby\Cms\HasFiles')) {
                array_walk_recursive($blocks, function (&$value, $key) use ($model) {
                    if (is_int($key) && is_string($value)) {
                        if($file = $model->file($value)) {
                            $value = $file->url();
                        }
                    }
                });
            }

            return $blocks;
        }
    ],
]);

Should at least work in “top level” blocks (wouldn’t work in nested blocks)

3 Likes

Hey @rasteiner, I made a working version which does exactly what I’m looking for but have hit an error now where I get the following The method “Kirby\Cms\Field::toblockshelper()” is not allowed in the API context, I have only just got this error so currently having a look to see if I can get around it.

Really appreciate the code above though!

Same with the example you provided too :frowning:

that’s strange:

And the plugin explicitly allows custom field methods:

1 Like

Are you sure the method gets registered?

could you like var_dump the registered methods somewhere in a template:

var_dump(array_keys(Field::$methods));

?

1 Like

Yep, the method is definitely registered, it outputs in the template fine.

I guess the plugin installation isn’t like more than a year old?

1 Like

It’s on the latest starter kit version, latest KQL version too.

It’s fixed! @rasteiner, thanks for your help! as it turned out I’d made a schoolboy error elsewhere!

Hi,

In my case I used a collection to return all the images of a page:
site/collections/notes.php

<?php

return function ($site) {
    $img_collection = [];
    foreach ($site->find('notes')->children()->images() as $image):
        $img_collection[$image->filename()] = str_replace($site->url(), '', $image->url());
    endforeach;
    return $img_collection;
};

… and query this collection with KQL

...
query: "site",
      select: {
        title: "site.title",
        url: "site.websiteurl",
        urlcms: "site.url",
        downloads: "site.downloads.toFile",
        baseline: "site.baseline",
        baselinetwo: "site.baselinetwo",
        info: "site.info",
        topmenu: "site.topmenu.toStructure",
        blockscollections: "kirby.collection('notes')",
...