Een veel voorkomend patroon voor een headless CMS is dat je sommige data op elke pagina nodig hebt. Denk hierbij aan de WordPress site titel en slogan, menu's, footer content of bepaalde CMS instellingen. Hier hebben wij een custom example/v1/common
endpoint voor gemaakt. In dit endpoint halen we in ons voorbeeld alle WordPress navigatie menu's op, alle Advanced Custom Fields opties en alle nuttige WordPress instellingen:
namespace App\Controllers;
class RestAPI
{
/**
* Register custom Rest API endpoints
*/
public static function register_api_routes()
{
register_rest_route(
'example/v1',
'/common', [
'methods' => 'GET',
'callback' => ['App\Controllers\RestAPI', 'get_common_data'],
'permission_callback' => '__return_true'
]
);
}
/**
* Get common data
*/
public static function get_common_data($request)
{
$common = [
'menus' => \App\Controllers\Menus::get_nav_menus(), // Get WP Nav Menus
'options' => get_fields('options'), // Get ACF Options
'settings' => self::get_global_settings($request) // Get WP Settings
];
return $common;
}
/**
* Get global site settings
*/
public static function get_global_settings($request)
{
$settings = [
'name' => get_option('blogname'),
'description' => get_option('blogdescription'),
'home' => \App\make_relative_url(home_url('/')),
'date_format' => get_option('date_format'),
'time_format' => get_option('time_format'),
'gmt_offset' => get_option('gmt_offset'),
'timezone' => get_option('timezone_string'),
'start_of_week' => get_option('start_of_week'),
'posts_per_page' => get_option('posts_per_page'),
'wp_max_upload_size' => wp_max_upload_size()
];
return $settings;
}
}
Roep de functie aan vanuit je functions.php:
add_action('rest_api_init', ['App\Controllers\RestAPI', 'register_api_routes']);
Om alle Wordpress menu's op te halen en op te maken voor een headless setup, hebben we een aparte PHP-klasse Menus gemaakt:
namespace App\Controllers;
class Menus
{
/**
* Construct Menu Item for WP Rest response
*/
public static function add_menu_item($item = null)
{
$menu_item = [];
if (!empty($item)) {
$menu_item['ID'] = $item->ID;
$menu_item['title'] = $item->title;
$menu_item['url'] = \App\make_relative_url($item->url);
if ($item->classes) {
$menu_item['classes'] = $item->classes;
}
if ($item->description) {
$menu_item['desc'] = $item->description;
}
if ($item->target) {
$menu_item['target'] = $item->target;
}
if ($item->object) {
$menu_item['object'] = $item->object;
}
if ($item->object_id && $item->object !== 'custom') {
$post = get_post($item->object_id);
$status = $post->post_status;
$parent = $post->post_parent ? get_post($post->post_parent) : null;
$parent_post = $post->post_parent;
if ($parent) {
$parent_post = [
'id' => (int) $parent->ID,
'slug' => $parent->post_name
];
}
if ($status === 'private') {
$menu_item['url'] = get_home_url() . ($parent ? '/' . $parent->post_name : '') .'/'. $post->post_name;
}
$menu_item['object'] = [
'id' => (int) $item->object_id,
'slug' => $post->post_name,
'status' => $status,
'parent' => $parent_post
];
}
}
return $menu_item;
}
/**
* Get WP Nav Menus
*/
public static function get_nav_menus()
{
$nav_menus = [];
$locations = get_nav_menu_locations();
foreach ($locations as $name => $menu_id) {
$menu_array = wp_get_nav_menu_items($menu_id);
$menu_items = [];
if ($menu_array) {
foreach ($menu_array as $item) {
// Parents
if (empty($item->menu_item_parent)) {
$menu_items[$item->ID] = self::add_menu_item($item);
// Children
} else {
$parent_id = $item->menu_item_parent;
$parent_exist = get_post_status($parent_id) !== FALSE;
if ($parent_exist) {
$submenu = [];
$submenu[$item->ID] = self::add_menu_item($item);
$menu_items[$parent_id]['children'][] = $submenu[$item->ID];
}
}
}
$nav_menu = array_values($menu_items);
$nav_menus[$name] = $nav_menu;
}
}
return $nav_menus;
}
}
Deze common data is nu beschikbaar via het custom endpoint:
https://cms.example.test/wp-json/example/v1/common/
en zal, afhankelijk van het door jou aantal gedefiniëerde menu's en ACF-opties er ongeveer zo uitzien:
{
"menus": {
"header_menu": [
{
"ID": 1058,
"classes": [""],
"object": {
"id": 21,
"parent": 0,
"slug": "contact",
"status": "publish"
},
"title": "Contact",
"url": "/contact/"
}
],
"main_menu": [
{
"ID": 513,
"classes": [""],
"object": {
"id": 17,
"parent": 0,
"slug": "over-ons",
"status": "publish"
},
"title": "Over ons",
"url": "/over-ons/"
},
{
"ID": 514,
"classes": [""],
"object": {
"id": 12,
"parent": 0,
"slug": "werk",
"status": "publish"
},
"title": "Werk",
"url": "/werk/"
},
{
"ID": 1063,
"children": [
{
"ID": 1072,
"classes": [""],
"object": {
"id": 25,
"parent": 0,
"slug": "visual-consultancy",
"status": "publish"
},
"title": "Visual consultancy",
"url": "/diensten/visual-consultancy/"
},
{
"ID": 1064,
"classes": [""],
"object": {
"id": 26,
"parent": 0,
"slug": "animatie",
"status": "publish"
},
"title": "Animatie",
"url": "/diensten/animatie/"
},
{
"ID": 1066,
"classes": [""],
"object": {
"id": 24,
"parent": 0,
"slug": "datavisualisatie",
"status": "publish"
},
"title": "Datavisualisatie",
"url": "/diensten/datavisualisatie/"
}
],
"classes": [""],
"object": "custom",
"title": "Diensten",
"url": "#"
},
{
"ID": 512,
"classes": [""],
"object": {
"id": 19,
"parent": 0,
"slug": "werken-bij",
"status": "publish"
},
"title": "Werken bij",
"url": "/werken-bij/"
},
{
"ID": 1062,
"classes": [""],
"object": {
"id": 1059,
"parent": 0,
"slug": "artikelen",
"status": "publish"
},
"title": "Artikelen",
"url": "/artikelen/"
},
{
"ID": 511,
"classes": [""],
"object": {
"id": 21,
"parent": 0,
"slug": "contact",
"status": "publish"
},
"title": "Contact",
"url": "/contact/"
}
],
"service_menu": [
{
"ID": 516,
"classes": [""],
"object": {
"id": 15,
"parent": 0,
"slug": "algemene-voorwaarden",
"status": "publish"
},
"title": "Algemene voorwaarden",
"url": "/algemene-voorwaarden/"
},
{
"ID": 517,
"classes": [""],
"object": {
"id": 3,
"parent": 0,
"slug": "privacybeleid",
"status": "publish"
},
"title": "Privacybeleid",
"url": "/privacybeleid/"
}
]
},
"options": {
"contact_details": {
"address": "Leidsekade 90",
"city": "Amsterdam",
"company": "My Company",
"department": "Amsterdam",
"email": "[email protected]",
"phone": "+31 20 123 45 67"
},
"four_oh_four": {
"home_link": "Terug naar home",
"title": "Deze pagina kunnen we niet vinden."
},
"social_media": {
"channels": [
{
"icon": {
"class": "fa-brands fa-instagram",
"element": "<i class=\"fa-brands fa-instagram\" aria-hidden=\"true\"></i>",
"hex": "\\f16d",
"id": "instagram",
"prefix": "fa-brands",
"style": "brands",
"unicode": ""
},
"name": "Instagram",
"url": "https://www.instagram.com/mycompany/"
},
{
"icon": {
"class": "fa-brands fa-linkedin-in",
"element": "<i class=\"fa-brands fa-linkedin-in\" aria-hidden=\"true\"></i>",
"hex": "\\f0e1",
"id": "linkedin-in",
"prefix": "fa-brands",
"style": "brands",
"unicode": ""
},
"name": "Linkedin",
"url": "https://www.linkedin.com/company/mycompany/"
},
{
"icon": {
"class": "fa-brands fa-twitter",
"element": "<i class=\"fa-brands fa-twitter\" aria-hidden=\"true\"></i>",
"hex": "\\f099",
"id": "twitter",
"prefix": "fa-brands",
"style": "brands",
"unicode": ""
},
"name": "Twitter",
"url": "https://twitter.com/mycompany/"
}
]
}
},
"settings": {
"date_format": "F j, Y",
"description": "Just another wordpress site",
"gmt_offset": 2,
"home": "/",
"name": "My Site",
"posts_per_page": "10",
"start_of_week": "1",
"time_format": "g:i a",
"timezone": "Europe/Amsterdam",
"wp_max_upload_size": 104857600
}
}