verbb / vizy

A flexible visual editor for Craft CMS
Other
44 stars 8 forks source link

GQL: resolved destinations of links to entries are only available in rendered HTML output #124

Closed tremby closed 1 year ago

tremby commented 2 years ago

Description

The stuff I get back from GQL is heading to a React application, so I am not querying for rendered HTML. I'm getting the data back in a structured form instead, which in most cases with the current API means getting it from the rawNode field.

I've found that URLs for entry links are not resolved as part of the rawNode data structure (I'm getting eg http://my-host/entry-url#entry:42@1 back). While this might be intended (it's raw, after all), it looks like there's nowhere I can get the resolved version except from the HTML field, unless I make a separate query for any entry IDs which I've seen mentioned to get their URLs, or I keep a map in memory. I'd rather not take either of those routes.

I can think of two things which would help:

Steps to reproduce

  1. Have a link to an entry.
  2. Query for that entry via GQL, and look at all output fields, in particular html and rawNode.

Additional info

Additional context

engram-design commented 2 years ago

So typically, you shouldn't be using rawNodes or rawNode for Vizy blocks. It's fine for the WYSIWYG nodes as they're pretty simple. The specification for rawNode is that it's untouched, straight from the database.

This poses issues for Vizy blocks, which contain complex fields, such as element fields. The raw JSON only stores a reference to the element(s) ID and siteId. This is useless information in your front-end, because you'll want the element(s) title, ID, URL, etc.

Good suggestions though - I'll have a think about which one to pick! Gut feeling will be the first option, and probably just automatic.

tremby commented 2 years ago

That's why I said "in most cases"! For Vizy blocks I'm querying the additional fields I know I'll need. That's working fine for me. The reason I'm using rawNode for almost everything else is that I'd need to parse JSON to grab nested content either way, since other than the rendered HTML, rawNode and content are the only places to get it. May as well just parse one blob of JSON for each, and have (almost) everything I need.

Great, thank you. Yes, any of the suggestions I made would do the trick.

Another suggestion would be to resolve the URLs in the content field but leave rawNode as is. (It'd mean I need to rewrite some code but I may need to anyway; that's not so terrible.)

tremby commented 2 years ago

Though... as I mentioned above, I think most flexible would be a list of referenced elements.

In my case I could grab references { id siteId uri url } and then on coming to (unresolved) URLs parse out the #entry:\d+@\d+ and get the matching reference from the list I retrieved. That would be more useful for my particular use case than the resolved URLs alone, because that way I know with 100% confidence whether it's a link local to my Nextjs app (which I then enhance with a single-page-app-style link), or it's on one of the other sites of this multisite deployment (which will be a normal anchor).

Without that, my site needs to try to determine that based on the URL, which isn't always quite as simple as you'd imagine.

tremby commented 2 years ago

The more I think about this, the more I think a references field would be ideal.

I'm imagining that each node would have references available, which would list all references used by it or its children.

I could query

nodes {
  ...
  references {
    key
    element {
      siteId
      id
      ...on pages_default_Entry {
        uri
      }
      ...on images_Asset {
        alt
        width
        height
        url @whatever-transform
        lqip: url @other-transform
      }
    }
  }
}

which would give something like

{
  "nodes": [
    {
      ...
      "references": [
        {
          "key": "http://localhost:3000/some/page#entry:11@1",
          "element": {
            "siteId": 1,
            "id": 11,
            "uri": "some/page"
          }
        },
        {
          "key": "https://url.to/my/image/which/vizy/returned.jpg",
          "element": {
            "siteId": 1,
            "id": 98,
            "alt": "A kitten",
            "width": 6000,
            "height": 4000,
            "url": "https://url.to/my/image/transformed-how-i-like/or/maybe-a-srcset.jpg",
            "lqip": "data:image/png;base64,abc123abc123=="
          }
        }
      ]
    }
  ]
}

Then when parsing the data structure and rendering to React nodes (or whatever) I can look up each link destination or image src in this list and I have all the extra data I needed. If a URL doesn't appear (it might be an external link, for example) I just leave it as is.

Is something like that feasible?

engram-design commented 2 years ago

It's something we can look at for sure - I'll have to give it some further thought on how best to collect elements in use. There's some instances where it could get hairy:

tremby commented 2 years ago

I didn't think about Vizy block nodes or nested Vizy fields. The main blocker for me is getting in-site link destinations out of paragraphs etc. If this field only appeared on nodes of specific types (paragraph, and anything else which a link mark can find its way into), that'd do the trick as far as I'm concerned, since you already provided a link to the asset for image blocks (though I'm waiting for that to make it in to the craft 4 branch).

engram-design commented 1 year ago

Updated in 2.1.0