Retrieving linked content

Sometimes your content is too awesome to be contained in a single item. Other times, you want to reuse bits of your content in various places. In other cases, you want to show how different pieces of content are related, such as who wrote an article. In situations like these, you're going to want to structure your content as separate items and then use linked items to show their connections.

This tutorial will show you how to retrieve your content with its relationships through the example of authors linked to articles. To go through the example yourself, first create and link authors in Kentico Cloud.

Table of contents

    How linked items help you

    One of the benefits of using an API-first CMS like Kentico Cloud is that your content is all structured and ready to be reused. You can define many elements for a given content item and use different ones in different contexts. For example, you might have a full article with catchy copy and captivating images, but only want to use its title and a short description in a list of articles. When you put your content into various structured items and then link those together to build relationships, you can easily define what gets used where.

    Some benefits from having content in separate items:

    • You can have separate workflows for separate pieces of content. For example, you can have a homepage with many testimonials and have some published and some in other steps.
    • You can reuse the items so you only have to edit content once for the changes to appear in many places. For example, you can have an author's bio appear on all the articles they've written.
    • You don't need to plan exactly what content will be included in a given section - you only need to give the opportunity to add linked items. For example, you can have a product listing where you later decide which products will appear and in which order.

    With Kentico Cloud, you can link content together in two ways:

    1. Using a Linked items element – here, you can limit the items, including what types can be included and how many. This way is covered here.
    2. In a Rich text element – here, your items are included within the text, which requires additional work from your app to render them. Read how to deal with structure in your rich text.

    This article will look at how to use linked items to add an author with a name and bio to your articles. You might do this if you have an author who is writing multiple articles and wants the chance to edit their bio separately. If you have not yet done so, you should create at least one author and link it to an article.

    Adding your relationships to your model

    If you're using strongly typed models, you should remember to add your Author model as well as add a linked items element to your Simple Article model.

    The following example shows the Author model in various languages.

    • Swift
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import ObjectMapper import KenticoCloud public class Author: Mappable { var name: TextElement? var bio: RichTextElement? public required init?(map: Map){ let mapper = MapElement.init(map: map) name = mapper.map(elementName: "name", elementType: TextElement.self) bio = mapper.map(elementName: "bio", elementType: RichTextElement.self) } public func mapping(map: Map) { } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import ObjectMapper import KenticoCloud public class Author: Mappable { var name: TextElement? var bio: RichTextElement? public required init?(map: Map){ let mapper = MapElement.init(map: map) name = mapper.map(elementName: "name", elementType: TextElement.self) bio = mapper.map(elementName: "bio", elementType: RichTextElement.self) } public func mapping(map: Map) { } }
    • Java
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import com.kenticocloud.delivery.ContentItemMapping; import com.kenticocloud.delivery.ElementMapping; import com.kenticocloud.delivery.System; import java.lang.String; import java.util.List; @ContentItemMapping("author") public class Homepage { @ElementMapping("name") String name; @ElementMapping("bio") String bio; System system; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getBio() { return bio; } public void setBio(String bio) { this.bio = bio; } public System getSystem() { return system; } public void setSystem(System system) { this.system = system; } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import com.kenticocloud.delivery.ContentItemMapping; import com.kenticocloud.delivery.ElementMapping; import com.kenticocloud.delivery.System; import java.lang.String; import java.util.List; @ContentItemMapping("author") public class Homepage { @ElementMapping("name") String name; @ElementMapping("bio") String bio; System system; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getBio() { return bio; } public void setBio(String bio) { this.bio = bio; } public System getSystem() { return system; } public void setSystem(System system) { this.system = system; } }
    • C#
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models using System; using System.Collections.Generic; using KenticoCloud.Delivery; namespace KenticoCloudModels { public partial class Author { public const string Codename = "author"; public const string NameCodename = "name"; public const string BioCodename = "bio"; public string Name { get; set; } public IRichTextContent Bio { get; set; } } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models using System; using System.Collections.Generic; using KenticoCloud.Delivery; namespace KenticoCloudModels { public partial class Author { public const string Codename = "author"; public const string NameCodename = "name"; public const string BioCodename = "bio"; public string Name { get; set; } public IRichTextContent Bio { get; set; } } }
    • TypeScript
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import { ContentItem, Elements } from "kentico-cloud-delivery"; export class Author extends ContentItem { public name: Elements.TextElement; public bio: Elements.RichTextElement; }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import { ContentItem, Elements } from "kentico-cloud-delivery"; export class Author extends ContentItem { public name: Elements.TextElement; public bio: Elements.RichTextElement; }

    The following examples show a Simple Article content type with a title, body, and author.

    • Swift
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import ObjectMapper import KenticoCloud class SimpleArticle: Mappable { var title: TextElement? var body: RichTextElement? var author: LinkedItemsElement? required init?(map: Map){ let mapper = MapElement.init(map: map) title = mapper.map(elementName: "title", elementType: TextElement.self) body = mapper.map(elementName: "body", elementType: RichTextElement.self) author = mapper.map(elementName: "author", elementType: LinkedItemsElement.self) } func mapping(map: Map) { } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import ObjectMapper import KenticoCloud class SimpleArticle: Mappable { var title: TextElement? var body: RichTextElement? var author: LinkedItemsElement? required init?(map: Map){ let mapper = MapElement.init(map: map) title = mapper.map(elementName: "title", elementType: TextElement.self) body = mapper.map(elementName: "body", elementType: RichTextElement.self) author = mapper.map(elementName: "author", elementType: LinkedItemsElement.self) } func mapping(map: Map) { } }
    • Java
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import com.kenticocloud.delivery.ContentItemMapping; import com.kenticocloud.delivery.ElementMapping; import com.kenticocloud.delivery.System; import java.lang.String; import java.util.List; @ContentItemMapping("simple_article") public class Homepage { @ElementMapping("title") String title; @ElementMapping("body") String body; @ContentItemMapping("author") List<ContentItem> author; System system; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public List<ContentItem> getAuthor() { return author; } public void setAuthor(List<ContentItem> author) { this.author = author; } public System getSystem() { return system; } public void setSystem(System system) { this.system = system; } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import com.kenticocloud.delivery.ContentItemMapping; import com.kenticocloud.delivery.ElementMapping; import com.kenticocloud.delivery.System; import java.lang.String; import java.util.List; @ContentItemMapping("simple_article") public class Homepage { @ElementMapping("title") String title; @ElementMapping("body") String body; @ContentItemMapping("author") List<ContentItem> author; System system; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public List<ContentItem> getAuthor() { return author; } public void setAuthor(List<ContentItem> author) { this.author = author; } public System getSystem() { return system; } public void setSystem(System system) { this.system = system; } }
    • C#
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models using System; using System.Collections.Generic; using KenticoCloud.Delivery; namespace KenticoCloudModels { public partial class SimpleArticle { public const string Codename = "simple_article"; public const string TitleCodename = "title"; public const string BodyCodename = "body"; public const string AuthorCodename = "author"; public string Title { get; set; } public string Body { get; set; } public IEnumerable<object> Author { get; set; } public ContentItemSystemAttributes System { get; set; } } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models using System; using System.Collections.Generic; using KenticoCloud.Delivery; namespace KenticoCloudModels { public partial class SimpleArticle { public const string Codename = "simple_article"; public const string TitleCodename = "title"; public const string BodyCodename = "body"; public const string AuthorCodename = "author"; public string Title { get; set; } public string Body { get; set; } public IEnumerable<object> Author { get; set; } public ContentItemSystemAttributes System { get; set; } } }
    • TypeScript
    import { ContentItem, Elements } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class SimpleArticle extends ContentItem { public title: Elements.TextElement; public body: Elements.RichTextElement; public author: ContentItem[]; }
    import { ContentItem, Elements } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class SimpleArticle extends ContentItem { public title: Elements.TextElement; public body: Elements.RichTextElement; public author: ContentItem[]; }

    Retrieving authors with articles

    Now, you can retrieve any articles along with their associated authors. While retrieving articles, make sure the depth parameter is higher than 0 (the default value is 1) so that the authors will be returned. Note that any other filtering or order you do to the article will not affect what items are returned within the Linked items element.

    The following examples show how to retrieve a single article (The Origin of Coffee) with its author (a linked item).

    • Swift
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import KenticoCloud let client = DeliveryClient.init(projectId: "e6d2946e-0e24-003f-a397-9d2d458c6d6e") client.getItem(modelType: SimpleArticle.self, itemName: "the_origin_of_coffee") { (isSuccess, deliveryItem, error) in if isSuccess { for authorCodeName in (itemResponse?.item?.author?.value)! { let author = itemResponse?.getLinkedItems(codename: authorCodeName, type: Author.self) } if let article = itemResponse.item { // Use your article with its author here } } else { if let error = error { print(error) } }
    // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models import KenticoCloud let client = DeliveryClient.init(projectId: "e6d2946e-0e24-003f-a397-9d2d458c6d6e") client.getItem(modelType: SimpleArticle.self, itemName: "the_origin_of_coffee") { (isSuccess, deliveryItem, error) in if isSuccess { for authorCodeName in (itemResponse?.item?.author?.value)! { let author = itemResponse?.getLinkedItems(codename: authorCodeName, type: Author.self) } if let article = itemResponse.item { // Use your article with its author here } } else { if let error = error { print(error) } }
    • Java
    import com.kenticocloud.delivery; DeliveryClient client = new DeliveryClient("e6d2946e-0e24-003f-a397-9d2d458c6d6e"); List<NameValuePair> params = DeliveryParameterBuilder.params().modularContentDepth(1).build(); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models SimpleArticleItem item = client.getItem("the_origin_of_coffee", SimpleArticleItem.class, params);
    import com.kenticocloud.delivery; DeliveryClient client = new DeliveryClient("e6d2946e-0e24-003f-a397-9d2d458c6d6e"); List<NameValuePair> params = DeliveryParameterBuilder.params().modularContentDepth(1).build(); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models SimpleArticleItem item = client.getItem("the_origin_of_coffee", SimpleArticleItem.class, params);
    • JavaScript
    const KenticoCloud = require("kentico-cloud-delivery"); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models class SimpleArticle extends KenticoCloud.ContentItem { constructor() { super(); } } class Author extends KenticoCloud.ContentItem { constructor() { super(); } } const deliveryClient = new KenticoCloud.DeliveryClient({ projectId: "e6d2946e-0e24-003f-a397-9d2d458c6d6e", typeResolvers: [ new KenticoCloud.TypeResolver("author", (rawData) => new Author()) ] }); deliveryClient.item("the_origin_of_coffee") .depthParameter(1) .toObservable() .subscribe(response => console.log(response.item));
    const KenticoCloud = require("kentico-cloud-delivery"); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models class SimpleArticle extends KenticoCloud.ContentItem { constructor() { super(); } } class Author extends KenticoCloud.ContentItem { constructor() { super(); } } const deliveryClient = new KenticoCloud.DeliveryClient({ projectId: "e6d2946e-0e24-003f-a397-9d2d458c6d6e", typeResolvers: [ new KenticoCloud.TypeResolver("author", (rawData) => new Author()) ] }); deliveryClient.item("the_origin_of_coffee") .depthParameter(1) .toObservable() .subscribe(response => console.log(response.item));
    • C#
    using KenticoCloud.Delivery; // Initializes a content delivery client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("e6d2946e-0e24-003f-a397-9d2d458c6d6e") .Build(); // Gets a specific article and its linked items // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models DeliveryItemResponse<SimpleArticle> response = await client.GetItemAsync<SimpleArticle>("the_origin_of_coffee", new DepthParameter(1) ); var item = response.Item;
    using KenticoCloud.Delivery; // Initializes a content delivery client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("e6d2946e-0e24-003f-a397-9d2d458c6d6e") .Build(); // Gets a specific article and its linked items // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models DeliveryItemResponse<SimpleArticle> response = await client.GetItemAsync<SimpleArticle>("the_origin_of_coffee", new DepthParameter(1) ); var item = response.Item;
    • PHP
    <?php // Defined by Composer to include required libraries require __DIR__ . "/vendor/autoload.php"; use KenticoCloud\Delivery\DeliveryClient; $client = new DeliveryClient("e6d2946e-0e24-003f-a397-9d2d458c6d6e"); $item = $client->getItem("the_origin_of_coffee", (new QueryParams()) ->depth(1));
    <?php // Defined by Composer to include required libraries require __DIR__ . "/vendor/autoload.php"; use KenticoCloud\Delivery\DeliveryClient; $client = new DeliveryClient("e6d2946e-0e24-003f-a397-9d2d458c6d6e"); $item = $client->getItem("the_origin_of_coffee", (new QueryParams()) ->depth(1));
    • cURL
    curl --request GET \ --url "https://deliver.kenticocloud.com/e6d2946e-0e24-003f-a397-9d2d458c6d6e/items/the_origin_of_coffee?depth=1" \ --header "content-type: application/json"
    curl --request GET \ --url "https://deliver.kenticocloud.com/e6d2946e-0e24-003f-a397-9d2d458c6d6e/items/the_origin_of_coffee?depth=1" \ --header "content-type: application/json"
    • Ruby
    require "delivery-sdk-ruby" delivery_client = KenticoCloud::Delivery::DeliveryClient.new project_id: "e6d2946e-0e24-003f-a397-9d2d458c6d6e" delivery_client.item("the_origin_of_coffee") .depth(1) .execute do |response| item = response.item puts response.json end
    require "delivery-sdk-ruby" delivery_client = KenticoCloud::Delivery::DeliveryClient.new project_id: "e6d2946e-0e24-003f-a397-9d2d458c6d6e" delivery_client.item("the_origin_of_coffee") .depth(1) .execute do |response| item = response.item puts response.json end
    • TypeScript
    import { ContentItem, DeliveryClient, Elements, TypeResolver } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class Author extends ContentItem { constructor(){ super(); } } export class SimpleArticle extends ContentItem { public title: Elements.TextElement; public summary: Elements.TextElement; public post_date: Elements.DateTimeElement; public teaser_image: Elements.AssetsElement; public author: Elements.LinkedItemsElement<Author>; constructor(){ super(); } } const deliveryClient = new DeliveryClient({ projectId: "e6d2946e-0e24-003f-a397-9d2d458c6d6e", typeResolvers: [ new TypeResolver("author", (rawData) => new Author) ] }); deliveryClient.item<SimpleArticle>("the_origin_of_coffee") .depthParameter(1) .toObservable() .subscribe(response => console.log(response.item));
    import { ContentItem, DeliveryClient, Elements, TypeResolver } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class Author extends ContentItem { constructor(){ super(); } } export class SimpleArticle extends ContentItem { public title: Elements.TextElement; public summary: Elements.TextElement; public post_date: Elements.DateTimeElement; public teaser_image: Elements.AssetsElement; public author: Elements.LinkedItemsElement<Author>; constructor(){ super(); } } const deliveryClient = new DeliveryClient({ projectId: "e6d2946e-0e24-003f-a397-9d2d458c6d6e", typeResolvers: [ new TypeResolver("author", (rawData) => new Author) ] }); deliveryClient.item<SimpleArticle>("the_origin_of_coffee") .depthParameter(1) .toObservable() .subscribe(response => console.log(response.item));

    In the default JSON response, the codename for your author (jane in our example) will appear in the value of your author element. If you had multiple authors, they would appear in the same order in the author element as they do in the UI.

    Jane's name and bio will appear in the modular_content collection under her codename. So to use Jane within your article, all you have to do is match the codename from the author element with the values you want to use from the jane collection.

    • JSON
    { "item": { "system": { "id": "9c5d229e-5dc1-4270-9dd1-ae15102c2c7f", "name": "The origin of coffee", "codename": "the_origin_of_coffee", "language": "en-US", "type": "simple_article", "sitemap_locations": [], "last_modified": "2018-12-21T16:53:56.1406719Z" }, "elements": { "title": { "type": "text", "name": "Title", "value": "The Origin of Coffee" }, "author": { "type": "modular_content", "name": "Author", "value": [ "jane" ] }, "body": { "type": "rich_text", "name": "Body", "images": {}, "links": {}, "modular_content": [], "value": "<p>The history of coffee is patchy and full of myth and hearsay.</p>\n<p>One of the most popular story goes that Kaldi, an Ethiopian goatherd, saw his goats eating coffee berries and as a result becoming elated. So, Kaldi then tried them himself and thus discovered the potential of coffee. And that"s where we got our name - Dancing Goat.</p>\n<p>Although it"s far more likely that uses for coffee were developed over time and were discovered by people tasting various parts of the cherry, the old fables do add a bit of romance and are very cute.</p>" } } }, "modular_content": { "jane": { "system": { "id": "23e460d4-aaad-4bab-8788-3b1f4a16b826", "name": "Jane", "codename": "jane", "language": "en-US", "type": "author", "sitemap_locations": [], "last_modified": "2018-12-21T10:33:34.0224902Z" }, "elements": { "name": { "type": "text", "name": "Name", "value": "Jane Doe" }, "bio": { "type": "rich_text", "name": "Bio", "images": {}, "links": {}, "modular_content": [], "value": "<p>Jane is a fun-loving writer who enjoys her coffee black as hell, strong as death, and sweet as love.</p>" } } } } }
    { "item": { "system": { "id": "9c5d229e-5dc1-4270-9dd1-ae15102c2c7f", "name": "The origin of coffee", "codename": "the_origin_of_coffee", "language": "en-US", "type": "simple_article", "sitemap_locations": [], "last_modified": "2018-12-21T16:53:56.1406719Z" }, "elements": { "title": { "type": "text", "name": "Title", "value": "The Origin of Coffee" }, "author": { "type": "modular_content", "name": "Author", "value": [ "jane" ] }, "body": { "type": "rich_text", "name": "Body", "images": {}, "links": {}, "modular_content": [], "value": "<p>The history of coffee is patchy and full of myth and hearsay.</p>\n<p>One of the most popular story goes that Kaldi, an Ethiopian goatherd, saw his goats eating coffee berries and as a result becoming elated. So, Kaldi then tried them himself and thus discovered the potential of coffee. And that"s where we got our name - Dancing Goat.</p>\n<p>Although it"s far more likely that uses for coffee were developed over time and were discovered by people tasting various parts of the cherry, the old fables do add a bit of romance and are very cute.</p>" } } }, "modular_content": { "jane": { "system": { "id": "23e460d4-aaad-4bab-8788-3b1f4a16b826", "name": "Jane", "codename": "jane", "language": "en-US", "type": "author", "sitemap_locations": [], "last_modified": "2018-12-21T10:33:34.0224902Z" }, "elements": { "name": { "type": "text", "name": "Name", "value": "Jane Doe" }, "bio": { "type": "rich_text", "name": "Bio", "images": {}, "links": {}, "modular_content": [], "value": "<p>Jane is a fun-loving writer who enjoys her coffee black as hell, strong as death, and sweet as love.</p>" } } } } }

    What's next?

    You've seen how to link content together through the example of adding an author to your articles. First, you model and create content in Kentico Cloud. Then, you adjust your models in your app and retrieve your articles with the new relationship. You can take a similar approach in linking other content, such as adding related articles.