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
143 stars 5 forks source link

Support for different selects based on object type (Block type/Page template/User role) #45

Open tobimori opened 1 year ago

tobimori commented 1 year ago

The issue

I want to access different properties based on block types. For an image, I might want to generate a srcset and get the alt text, but other blocks don't have an image, so the query would fail.

The previous solution would be to create a Block model with Kirby, with a toArray function. This would result in my "queries" being in two different locations - half of it in my frontend, and everything related to a specific Block type etc. would be in my backend.

Solution

I experimented with that idea here.

In short, I added a models property that allows to specify different selects based on the object type, similar to models in core.

const { result } = await fetchKql({
  query: 'page("home")',
  select: {
    title: true,
    blocks: {
      query: 'page.blocks.toBlocks',
      // needs select as potential fallback, empty array will return null if no matches
      // if not specified, query will not respect 'models' and return all fields
      select: ['type'],
      // 'models' as directory for different 'selects' based on class/block type
      models: {
        // 'select' for 'image' block type
        image: {
          type: true,
          image: 'block.image.toFile?.resize(200)',
          title: true
        },
        // 'select' for 'text' block type
        text: ['type', 'heading', 'text']
      }
    }
  }
})

This is a quick-and-dirty proof-of-concept, but it works for what I've tested it. Feel free to take any code from that repository for adding this feature.

eriksachse commented 1 year ago

Thanks. I still struggle with the query and my implementation for layouts specifically look like this:

layout: {
  query: "page.layout.toLayouts",
  select: {
    columns: {
      query: "layout.columns",
      select: {
        id: true,
        width: true,
        blocks: {
          select: {
            type: true,
            text: true,
            image: {
              query: "block.image.toFile?.resize(200)",
              select: {
                url: true,
                // Resize should be here
                // small: "image.resize('200').url",
              },
            },
          },
        },
      },
    },
  },
},

And I wonder if I could add different srcsets... That would be amazing.

tobimori commented 1 year ago

Pretty sure you should be able to something like this:

            image: {
              query: "block.image.toFile",
              select: {
                url: true,
                small: "file.resize('200').url",
                medium: "file.resize('400').url",
              },

Doesn't belong to this issue tho?

benwest commented 1 year ago

This is perfect, would love to see it in core - I guess it's not possible to implement as an additional plugin independent from KQL?

tobimori commented 1 year ago

This is perfect, would love to see it in core - I guess it's not possible to implement as an additional plugin independent from KQL?

nope, sadly would need to be integrated in KQL directly - similar to how I did it.

benwest commented 1 year ago

I've made a push at TS types for KQL, supporting your extended models query: https://github.com/benwest/kql-ts

If you are interested in using, testing or helping out, cool! Otherwise, thanks for the plugin.

tobimori commented 1 year ago

I've made a push at TS types for KQL, supporting your extended models query: benwest/kql-ts

If you are interested in using, testing or helping out, cool! Otherwise, thanks for the plugin.

Really nice! I'll check it out when I've time to work on my personal site again.

johannschopplich commented 1 year ago

Seems like we all have the same issue – resolving relations inside blocks. For my personal use-case, I have created a custom toResolvedBlocks field method to resolve images.

Via the Kirby config, we can define, which blocks actually contain images:

# /site/config/config.php
return [
    'blocksResolver' => [
        // Blocks that contain files fields
        'files' => [
            // Block name as key, field name as value
            // Resolve the built-in `image` field of the `image` block
            'image' => 'image'
        ]
    ]
];

But the models option is much smarter. 😄

tobimori commented 1 year ago

@bastianallgeier @distantnative I'd love to hear what you think about my approach. If it suits your needs, I'll happily submit a PR.

bastianallgeier commented 1 year ago

Sorry for the delay, but I actually really love the models idea! A PR would be great.

tobimori commented 1 year ago

A PR would be great.

@bastianallgeier

Do you mind taking a look at the plugin I linked above as a base for the PR? Will of course add unit tests, but just asking for feedback on the general implementation as I haven't completely understood the Kql code yet 🙂

bastianallgeier commented 1 year ago

@tobimori sorry, I completely missd the link to your experiment. The code looks super straight forward.

ovenum commented 10 months ago

Just started looking into KQL and this one of the first issues i ran into. I know work is focused on releasing Kirby v4 and i can’t wait for it : )

To make KQL usable with blocks the support for different and custom block types should be added to the plugin. Reading the issues for KQL it seems that this causes at least a fair amount of headaches.