How to expose Global Sets data in your Element API Endpoint.
This article covers both Craft 2 and Craft 3. The Craft 3 changes are at the bottom of the article.
I’ve used the Element API in Craft CMS to share data with a Vue.js app, create a headless CMS for a blog, and to share common information among a group of related sites.
The Element API makes it easy to expose your Craft-powered website’s data via JSON so other applications can consume it and use it.
The third example is what we’ll work on in this article.
I have a network of related sites that all have the same footer containing a list of links (for a “network” of sites). I want to manage this set of links with the Craft CMS installation of the anchor site and share the links with the other sites in the network.
I’m storing this set of links and display information (like headers and new column indicators) in a Global Set called Network (with the handle network
).
The Network Global Set has a single Matrix field called Sites (sites
) with this setup:
site
)siteName
), Plain TextsiteUrl
), Plain TextsectionHeader
)sectionTitle
), Plain Textcolumn
)newColumn
), LightswitchI’m organizing the Sites into sections (via a Section Header). I can control when a new column is created using g the New Column Lightswitch field. I want to ensure the same exact layout across all sites, so it made the most sense to include layout data in the Global Set.
In my video lesson on the Element API, I only worked with Entries. In this example we need to expose the data in a Global Set. The approach is similar but instead of using an Entries
element type we use a GlobalSet
element type.
Let’s code the Element API and see how it works.
If you’re not familiar with the basics of using Element API in Craft, please first review my video lesson on the topic.
We start with a basic Element API wrapper:
<?php
namespace Craft;
return [
'endpoints' => [
]
];
We define our first endpoint. It can be whatever we want, but let’s call it api/network.json
.
<?php
namespace Craft;
return [
'endpoints' => [
'api/network.json' =>
[
],
]
];
We need to decide what we want to return. Our network data is in a Global Set, so we need to set the elementType
for this endpoint to ElementType::GlobalSet
.
Then set the criteria for retrieving the Global Set by defining the handle of the set we want.
<?php
namespace Craft;
return [
'endpoints' => [
'api/network.json' =>
[
'elementType' => ElementType::GlobalSet
'criteria' => ['handle' => 'network'],
]
]
];
Now that we have our data, we need to transform it into a format that is acceptable as JSON. To do that we use a transformer. In the transformer, we create an anonymous function which passes in the GlobalSetModel
so Craft knows the data model it should use.
<?php
namespace Craft;
return [
'endpoints' => [
'api/network.json' =>
[
'elementType' => ElementType::GlobalSet
'criteria' => ['handle' => 'network'],
'transformer' => function(GlobalSetModel $globalSet)
{
// our data here
}
]
]
];
Inside of the transformer function we define our data as we want it to appear in the JSON output.
<?php
namespace Craft;
return [
'endpoints' => [
'api/network.json' =>
[
'elementType' => ElementType::GlobalSet
'criteria' => ['handle' => 'network'],
'transformer' => function(GlobalSetModel $globalSet)
{
$networkBlocks = [];
foreach ($globalSet->sites as $block) {
switch ($block->type->handle) {
case 'site':
$networkBlocks[] = [
array(
'siteName' => $block->siteName,
'siteUrl' => $block->siteUrl,
)
];
break;
case 'sectionHeader':
$networkBlocks[] = [
'sectionTitle' => $block->sectionTitle,
];
break;
case 'column':
$networkBlocks[] = [
'newColumn' => $block->newColumn,
];
}
}
}
]
]
];
We define an empty array called $networkBlocks
and then we fill up this array with our data.
At foreach ($globalSet->sites as $block) {
we iterate over each site row in the Matrix field. For each one we are checking the block type using a switch
statement.
If the block has more than one field, we assign its data to a nested array because we want those values to stick together. The keys we choose (e.g. siteName
and siteUrl
) will be used in the JSON.
We add the single field blocks to the main array with the keys set to the name we want to appear in the JSON.
To get our newly transformed data out, we need to return the array from the transformer function.
<?php
namespace Craft;
return [
'endpoints' => [
'api/network.json' =>
[
'elementType' => ElementType::GlobalSet
'criteria' => ['handle' => 'network'],
'transformer' => function(GlobalSetModel $globalSet)
{
$networkBlocks = [];
foreach ($globalSet->sites as $block) {
switch ($block->type->handle) {
case 'site':
$networkBlocks[] = [
array(
'siteName' => $block->siteName,
'siteUrl' => $block->siteUrl,
)
];
break;
case 'sectionHeader':
$networkBlocks[] = [
'sectionTitle' => $block->sectionTitle,
];
break;
case 'column':
$networkBlocks[] = [
'newColumn' => $block->newColumn,
];
}
}
return ['network' => $networkBlocks];
}
]
]
];
Let’s test the API and see what we get. If we coded it properly, we should see something like this at, for example, yoursite.com/api/network.json
:
{
"data": [
{
"network": [
{
"sectionTitle": "Insight"
},
[
{
"siteName": "Personal Blog",
"siteUrl": "http://ryanirelan.com/"
}
],
{
"sectionTitle": "Training"
},
[
{
"siteName": "Mijingo",
"siteUrl": "https://mijingo.com"
}
],
[
{
"siteName": "Up and Running with SVG",
"siteUrl": "http://svgtutorial.com"
}
],
[
{
"siteName": "Craft CMS Essentials",
"siteUrl": "http://craftcmsessentials.com"
}
],
{
"newColumn": "1"
},
{
"sectionTitle": "Running"
},
[
{
"siteName": "Ryan Runs",
"siteUrl": "http://www.ryanruns.com"
}
],
]
}
],
"meta": {
"pagination": {
"total": 1,
"count": 1,
"per_page": 100,
"current_page": 1,
"total_pages": 1,
"links": []
}
}
}
With our Global Set data exposed as JSON via the Element API, we’re now ready to pull it into our target sites.
To get this same approach to work in Craft 3 (in RC1 as a I write this), we need to make a couple adjustments.
First, the file name inside of the config
directory should be element-api.php
instead of the elementapi.php
name in Craft 2.
Second, at the top of the file we no longer need to specify the Craft namespace but instead specify the element type (or types) class name we plan to access. In our case we only need to access a Global Set, so the top of our file looks like this:
<?php
use craft\elements\GlobalSet;
</code></pre>
If we also want to fetch and return entries we'd would need to specify the Entry class name, too.
Next, we need to change where we refer to the Element Type and replace that with the Element Type class name we just added.
<pre class="language-php line-numbers"><code>
<?php
use craft\elements\GlobalSet;
return [
'endpoints' => [
'api/network.json' =>
[
'elementType' => GlobalSet::Class
'criteria' => ['handle' => 'network'],
'transformer' => function(GlobalSet $globalSet)
{
We also change the transformer to use GlobalSet
instead of GlobalSetModel
.
Everything else should work as-is!
If you need some assistance with Craft 3’s implementation of the Element API, you can refer to the official documentation.