getkirby / kql

Kirby's Query Language API combines the flexibility of Kirby's data structures, the power of GraphQL and the simplicity of REST.
https://getkirby.com
MIT License
145 stars 5 forks source link

Access file inside Blocks field #29

Closed eriksachse closed 3 years ago

eriksachse commented 3 years ago

Similar to issue #8 I want to access the files that are in my blocks field. This is my query:

    {
      query: "site.children",
      select: {
        title: true,
        url: true,
        slug: true,
        allarticles: {
          query: "page.allarticles.toBlocks",
          select: {
            text: true,
            image: {
              query: "block.image.toFile",
            },
          },
        },
      },
    }, 

The text works fine, but the image reproduces a null. I want to access the file because I want to have an srcset there.

Note: There are solutions with custom hooks written in the forum, but it would be nice to have it inside the query.

eriksachse commented 3 years ago

short-term solution

Since I don't think that this will be resolved anytime soon, I have found a nice way thanks to the people over there in the forum 🤡

Create a plugin in site/plugins/toBlocksCustom in the index.php copy the following:

<?php
use Kirby\Cms\App as Kirby;

Kirby::plugin('eriksachse/toBlocksCustom', [
    'fieldMethods' => [
        'toBlocksCustom' => 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->srcset([300, 800, 1024]);
                        }
                    }
                });
            }

            return $blocks;
        }
    ],
]);

It just checks if there are any images as block and returns multiple image sizes ready to use. Would love to see this in the docs!

marco-land commented 2 years ago

Hi @eriksachse, this works perfect, thanks for this!

Do you have any idea how this could be extended so that it also works if there are Blocks within the Blocks? As of now it works for me only in the first instance of blocks.

eriksachse commented 2 years ago

@marco-land

I'm not familiar with Kirby plugins etc., so my approach would be to go trough the entire snippet and check which statement is using which variable.

$model = $field->parent();
$blocks = $field->toBlocks()->toArray();

With $model, we'll soon check if the $page has any files. Usually it's all files inside the page. With $blocks we split the blocks into an array.

if (class_uses($model, 'Kirby\Cms\HasFiles')) { I think this is where you have your problem. If you go back one block, but have two blocks, then you are still inside the block, and have no files. I hope this sentence makes sense. With only one block going into the parent you have the $page model. Correct me if I'm wrong.

array_walk_recursive($blocks, function (&$value, $key) use ($model) {

array_walk_recursive — Apply a user function recursively to every member of an array. Whatever this means 👯

 if (is_int($key) && is_string($value)) {
 if($file = $model->file($value)) {
 $value = $file->srcset([300, 800, 1024]);
 }
}

This is checking if the file from the $page is inside your block. I think you'll have to create some variables and another statement. In the end it just resizes all the files that are in your $page. I have some other ideas in mind, will put themm into my next Next.js tutorial.

marco-land commented 2 years ago

Hi Erik,

thanks for your explanation, as of now I've used this solution:

'toBlocksCustom' => 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 = [
                    'filename' => $file->filename(),
                    'src' => $file->url(),
                    'lowres' => $file->resize(10, null, 50)->url(),
                    'medium' => $file->resize(1250, null, 70)->url(),
                    'srcset' => $file->srcset([600, 1000, 1400, 1800]),
                    'ratio' => $file->ratio(),
                  ];
                }
              }
              // find the blueprint block name within the blocks
              if ($key === 'blockparty') {
                $secondBlocks = json_decode($value);
                if (is_array($secondBlocks)) {
                  array_walk_recursive($secondBlocks, function (&$value, $key) use ($model) {
                    // I had to check for images because I also had other fields using files which caused errors
                    if ($value->type === 'image') {
                      if($file = $model->file($value->content->image[0]) ) {
                        $value->content = [
                          'image' => [
                            [
                              'filename' => $file->filename(),
                              'src' => $file->url(),
                              'lowres' => $file->resize(10, null, 50)->url(),
                              'medium' => $file->resize(1250, null, 70)->url(),
                              'srcset' => $file->srcset([600, 1000, 1400, 1800]),
                              'ratio' => $file->ratio(),
                            ]
                          ],
                          'alt' => $file->alt()->value(),
                          'caption' => $file->caption()->value(),
                        ];
                      }
                    } else {
                      $value = $value;
                    }
                  });
                  $value = $secondBlocks;
                }
              }
            });
          }
        return $blocks;

I am not so good at PHP but I think there is probably a better approach than checking for the second block blueprint name