Related
A single query can also fetch entries related to the ones it finds: their parents, children and siblings, their translations, and the entries they link to. Put a relation in select (or in include when you don't select), and it's resolved in the same query.
import {cms} from '@/cms'
import {Blog, BlogPost} from '@/schema'
import {Query} from 'alinea'
const blog = await cms.get({
type: Blog,
locale: 'en',
select: {
title: Query.title,
posts: Query.children({
type: BlogPost,
select: {title: Query.title, url: Query.url, date: BlogPost.publishDate},
orderBy: {desc: BlogPost.publishDate},
take: 5
})
}
})Every relation takes the same options as a query, such as type, filter, select, orderBy, skip and take. parent, previous and next return a single entry, the others a list. Add count: true to get the number of matches instead, as in Query.children({count: true}), or first: true to get a single entry (or null). Relations can be nested, for example children with their own children.
Tree relations
Query.parent(...): the parent entry, or nothing for entries at the top of a root.Query.parents(...): all ancestors, starting at the top of the root. Passdepthto get only the nearest ones:depth: 1is the parent.Query.children(...): the direct children. Passdepthto include deeper descendants too, returned as one flat list.Query.siblings(...): the other children of the same parent. AddincludeSelf: trueto include the entry itself.Query.previous(...)andQuery.next(...): the sibling right before or after the entry, in sidebar order, or nothing at the ends.Query.translations(...): the same entry in the other locales of the root. AddincludeSelf: trueto include the current locale, listed first.
Tree relations stay in the locale of the entry they start from (except translations, of course) and use the status of the outer query.
import {cms} from '@/cms'
import {BlogPost} from '@/schema'
import {Query} from 'alinea'
export async function postPage(url: string) {
return cms.first({
type: BlogPost,
url,
select: {
title: Query.title,
body: BlogPost.body,
// Breadcrumbs, from the top of the root down
breadcrumbs: Query.parents({
select: {title: Query.title, url: Query.url}
}),
// Pagination at the bottom of the post
previous: Query.previous({
type: BlogPost,
select: {title: Query.title, url: Query.url}
}),
next: Query.next({
type: BlogPost,
select: {title: Query.title, url: Query.url}
}),
// A language switcher
translations: Query.translations({
select: {locale: Query.locale, url: Query.url}
})
}
})
}siblings, previous and next find entries that share a parent. For an entry at the top level of a root, they find the other top-level entries of the same root (and locale).
Linked entries
Entry, Link, Image and File fields return the basics of what they link to. To get other fields of the linked entries, query through the field:
a single link field has
.first(query).a multiple link field has
.find(query),.first(query)and.count(query). Results keep the order the editor gave the links.
import {cms} from '@/cms'
import {Author, BlogPost} from '@/schema'
import {Query} from 'alinea'
const post = await cms.get({
type: BlogPost,
path: 'hello-world',
select: {
title: Query.title,
author: BlogPost.author.first({
select: {name: Query.title, bio: Author.bio, avatar: Author.avatar}
}),
categories: BlogPost.categories.find({
select: {title: Query.title, url: Query.url}
})
}
})Linked entries are looked up in the locale of the entry you queried, falling back to entries without a locale, so a link to an untranslated author keeps working from every translation of a post.
The other direction
Links are stored on the entry that links, so "all posts by this author" is a filter on the posts. Filter on the id of the target, stored as _entry:
import {cms} from '@/cms'
import {Author, BlogPost} from '@/schema'
import {Query} from 'alinea'
export async function authorPage(path: string) {
const author = await cms.get({
type: Author,
path,
select: {id: Query.id, name: Query.title, bio: Author.bio}
})
const posts = await cms.find({
type: BlogPost,
// Single link: has, multiple links: includes
filter: {author: {has: {_entry: author.id}}},
select: {title: Query.title, url: Query.url},
orderBy: {desc: BlogPost.publishDate}
})
return {author, posts}
}For multiple links, such as categories, use {categories: {includes: {_entry: id}}}. More combinations are in Filtering and the examples.