Global Sets in the Craft Element API

How to expose Global Sets data in your Element API Endpoint.

Image

This arti­cle cov­ers both Craft 2 and Craft 3. The Craft 3 changes are at the bot­tom of the article.

I’ve used the Ele­ment API in Craft CMS to share data with a Vue.js app, cre­ate a head­less CMS for a blog, and to share com­mon infor­ma­tion among a group of relat­ed sites.

The Ele­ment API makes it easy to expose your Craft-pow­ered website’s data via JSON so oth­er appli­ca­tions can con­sume it and use it. 

The third exam­ple is what we’ll work on in this article.

I have a net­work of relat­ed sites that all have the same foot­er con­tain­ing a list of links (for a net­work” of sites). I want to man­age this set of links with the Craft CMS instal­la­tion of the anchor site and share the links with the oth­er sites in the network.

I’m stor­ing this set of links and dis­play infor­ma­tion (like head­ers and new col­umn indi­ca­tors) in a Glob­al Set called Net­work (with the han­dle network).

The Net­work Glob­al Set has a sin­gle Matrix field called Sites (sites) with this setup:

  • Site (site)
    • Site Name (siteName), Plain Text
    • Site URl (siteUrl), Plain Text
  • Sec­tion Head­er (sectionHeader)
    • Sec­tion Title (sectionTitle), Plain Text
  • Col­umn (column)
    • New Col­umn (newColumn), Lightswitch

I’m orga­niz­ing the Sites into sec­tions (via a Sec­tion Head­er). I can con­trol when a new col­umn is cre­at­ed using g the New Col­umn Lightswitch field. I want to ensure the same exact lay­out across all sites, so it made the most sense to include lay­out data in the Glob­al Set.

In my video les­son on the Ele­ment API, I only worked with Entries. In this exam­ple we need to expose the data in a Glob­al Set. The approach is sim­i­lar but instead of using an Entries ele­ment type we use a GlobalSet ele­ment type. 

Let’s code the Ele­ment API and see how it works.

Cod­ing the Ele­ment API for Glob­al Sets

If you’re not famil­iar with the basics of using Ele­ment API in Craft, please first review my video les­son on the top­ic.

We start with a basic Ele­ment API wrapper:

<?php
namespace Craft;

return [
	'endpoints' => [
	]
];

We define our first end­point. It can be what­ev­er 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 net­work data is in a Glob­al Set, so we need to set the elementType for this end­point to ElementType::GlobalSet.

Then set the cri­te­ria for retriev­ing the Glob­al Set by defin­ing the han­dle 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 trans­form it into a for­mat that is accept­able as JSON. To do that we use a trans­former. In the trans­former, we cre­ate an anony­mous func­tion which pass­es in the GlobalSetModel so Craft knows the data mod­el 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 trans­former func­tion 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 emp­ty array called $networkBlocks and then we fill up this array with our data.

At foreach ($globalSet->sites as $block) { we iter­ate over each site row in the Matrix field. For each one we are check­ing the block type using a switch statement.

If the block has more than one field, we assign its data to a nest­ed array because we want those val­ues to stick togeth­er. The keys we choose (e.g. siteName and siteUrl) will be used in the JSON.

We add the sin­gle field blocks to the main array with the keys set to the name we want to appear in the JSON.

To get our new­ly trans­formed data out, we need to return the array from the trans­former 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 cod­ed it prop­er­ly, we should see some­thing like this at, for exam­ple, 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 Glob­al Set data exposed as JSON via the Ele­ment API, we’re now ready to pull it into our tar­get sites.

Mak­ing it Work in Craft 3

To get this same approach to work in Craft 3 (in RC1 as a I write this), we need to make a cou­ple adjustments.

First, the file name inside of the config direc­to­ry should be element-api.php instead of the elementapi.php name in Craft 2

Sec­ond, at the top of the file we no longer need to spec­i­fy the Craft name­space but instead spec­i­fy the ele­ment type (or types) class name we plan to access. In our case we only need to access a Glob­al 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 trans­former to use GlobalSet instead of GlobalSetModel.

Every­thing else should work as-is!

If you need some assis­tance with Craft 3’s imple­men­ta­tion of the Ele­ment API, you can refer to the offi­cial doc­u­men­ta­tion.