PROBLEM:
-
I need a tagging content-type for my site based on physical location, call it locations
. My spec requires each location have it's own page along with being a tag on other content-types. This rules out the Prismic tag system, because it cannot be used as a content-type or generate pages strangely.
-
The recommendation from Prismic if you need true tags is to create a custom content-type as a tag. Once that is set up, you can model a group
in another content-type to create a multi-select of content-relationships. This allows you to tag between content-types now (ie, 'I want to tag a news
item with three locations
').
-
As noted in this thread and in the Prismic docs, if you're using GraphQL you are out of luck. You can't look inside a group
with aGraphQL union for whatever reason. So, to recap, you can build a multi-tag set up on the admin side but you just can't even query for it.
Obviously, this sucks. It seems like a huge oversight for a CMS but we're all developers here and are used to encountering weird circumstances. So let's get into a solution.
SOLUTION
If you keep digging in the Prismic docs, you will find that you can query for a group of content relationships inside of content-type...but it only works in REST for whatever reason. So to actually fetch tags articles within a content-type, you need to use both GraphQL and then do a callback with id
of the tag INTO a REST query. That's a mouthful - let's break it down since Prismic doesn't seem to want to.
CODE
Let's assume you're on an individual instance of a tag page (/locations/[uid].js
) and want to display 3 news articles tagged with that location. Getting the location is pretty easy in GraphQL - take note of the id
inside of _meta
- you will need that in a second.
query LocationByUID($uid: String!, $lang: String!) {
locations(uid: $uid, lang: $lang) {
header_image
location
intro
_meta {
uid
id
type
}
}
}
You want to take that response and store it into a response variable - like:
const locationResults = [ data ]
The part of the response you need for the REST call would be locations._meta.id
in my example, because content-relationship queries require using the id
, not the uid
- again this isn't explained, go take it up with Prismic.
The REST query to get news
items by the location
is pretty weird looking, very atypical at least to me. Pay attention to the Prismic.Predicates.at('my.news.locations.location', data.locations._meta.id)
line, which effectively means 'look for news items tagged by the location id from the last query` - effectively a GraphQL callback:
await Prismic.getApi(REF_API_URL).then(
function(api) {
return api.query(
[
Prismic.Predicates.at('document.type', 'news'),
Prismic.Predicates.at('my.news.locations.location', data.locations._meta.id),
],
{ orderings : '[my.news.publication_date desc]' }
);
}).then(
function(resNews) {
locationResults.push(resNews.results)
}
);
Taken altogether, you can now get a single location
tag page with n number of news
items tagged by location (using a multi-select tagging system):
export async function getSingleLocationsPage(uid, previewData) {
const data = await fetchAPI(`
query LocationByUID($uid: String!, $lang: String!) {
locations(uid: $uid, lang: $lang) {
header_image
location
intro
_meta {
uid
id
type
}
}
}
`,
{
previewData,
variables: {
uid,
lang: API_LOCALE,
},
}
)
// Due to limitations with the GraphQL endpoint (can't query tags in groups)
// We need to do a second call to get linked news, topics and grants per location
// Build array for single location data
const locationResults = [ data ]
await Prismic.getApi(REF_API_URL).then(
function(api) {
return api.query(
[
Prismic.Predicates.at('document.type', 'news'),
Prismic.Predicates.at('my.news.locations.location', data.locations._meta.id),
],
{ orderings : '[my.news.publication_date desc]' }
);
}).then(
function(resNews) {
locationResults.push(resNews.results)
}
);
return locationResults
}
This works pretty well in my testing but obviously feel hacky. Good luck coding peeps.