5

I have an array:

 $cats = array('Hobbes', 'Simba', 'Grumpy Cat');

I would like to create a custom endpoint called "http://example.com/cats". Whenever you visit this URL, have it output the array as JSON:

 header('Content-Type: application/json');
 $cats = array('Hobbes', 'Simba', 'Grumpy Cat');
 echo json_encode($cats);

Ideally creating these on the fly in functions.php. So creating "/dogs" or "/birds".

  • You may want to accept the answer with the rewrite rules and custom endpoint ;) – kaiser Feb 18 '15 at 22:34
5

You can use pre_get_posts as suggested but simply check the global $wp object for the request url.. like so:

add_action( 'pre_get_posts', function ($query ){
    global $wp;

    if ( !is_admin() && $query->is_main_query() ) {
        if ($wp->request == 'cats'){
            header('Content-Type: application/json');
            $cats = array('Hobbes', 'Simba', 'Grumpy Cat');
            echo json_encode($cats);
            exit;
        }
    }
});

(PHP 5.3+ in this example)

Note 1: The $query->is_main_query() test is important as it will prevent it from running again in subsequent context of WP_Query instances (eg. if you use a WP_Query to fetch your list of cats, you will end up with an infinite nested loop). is_admin() isn't critical but it's a good practice to use.

Note 2: Wordpress seems to trim trailing slashes in request so this works for /cats & /cats/ the same way

Note 3: You must exit; in your handler otherwise it will keep executing the regular wordpress workflow and likely show a 404 page.

  • 1
    This is dynamite... I think this is it. – envysea Feb 18 '15 at 22:09
  • Added a few notes about the code – Ben Feb 18 '15 at 22:10
  • 1
    FYI, for the request, I use ".cats" to avoid any conflicts with the URI. It works great. – envysea Feb 20 '15 at 15:43
8

This is the approach to add a rewrite rule for swapping out the template that WP wants to render (usually the 404 template) with something else. In your case you're just wanting to die with some JSON data so there are a bunch of different ways to do this. This is the "by the book" approach to adding a rewrite rule instead of a hack to check the URL on your own. Going to post this here in the event someone needs to see how this could be done to swap out the template or die with some encoded data.

<?php

function cats_rewrite() {
    add_rewrite_rule( 'cats/(.*)/?', 'index.php?cats=$matches[1]', 'top' );
}
add_action( 'init', 'cats_rewrite' );

function filter_query_vars( $query_vars ) {
    $query_vars[] = 'cats';

    return $query_vars;
}
add_filter( 'query_vars', 'filter_query_vars' );

function my_template_include( $template ) {
    global $wp_query;

    // You could normally swap out the template WP wants to use here, but we'll just die
    if ( isset( $wp_query->query_vars['cats'] ) && ! is_page() && ! is_single() ) {
        $wp_query->is_404 = false;
        $wp_query->is_archive = true;
        $wp_query->is_category = true;
        $cats = array( 'Hobbes', 'Simba', 'Grumpy Cat' );
        header( 'Content-Type: application/json' );
        die( json_encode( $cats ) );
    } else {
        return $template;
    }
}
add_filter( 'template_include', 'my_template_include' );

Important: don't forget to flush your permalink settings. Just visit (Admin) Settings > Permalinks. You don't even need to click save as the rewrite settings are "flushed" when you visit the admin page.

  • This should be the chosen answer. Still, I'd suggest to use a custom ENDPOINT for that. – kaiser Feb 18 '15 at 22:32
  • Kasier, care to elaborate? Happy to swap the chosen answer – envysea Feb 18 '15 at 22:44
  • 1
    I can just point you to the SERPs for endpoints. In short: it's about the last part of the URl like http://example.com/author/json. You can add endpoints to everything (to add JSON output for every type of request) or just to specific ones (like EP_AUTHOR for e.g.). – kaiser Feb 18 '15 at 23:08
3

The easiest way I can think of (and the only one) is create a page with the permalink you're indicating, create a custom template in your theme folder, let's say it's json_output.php with the following content:

<?php
/*
* Template Name: JSON Output
*/
header('Content-Type: application/json');
$cats = array('Hobbes', 'Simba', 'Grumpy Cat');
echo json_encode($cats);

When creating the page in the wordpress admin, select your new created template, in this case it has to be named JSON Output

enter image description here

There you go, publish the page and you'll get what you want: enter image description here

Hope it helps

  • 1
    It doesn't meet the functions.php requirement but it's the best solution in terms of performance and maintenance. – pixeline Feb 18 '15 at 21:59
  • Yeah, I would like this to be in functions.php. Thanks though – envysea Feb 18 '15 at 22:00
1

It seems to me you could use pre_get_posts action hook to modify the query, but it feels a bit of a hack. Anyways, something like this ?

add_action( 'pre_get_posts', 'cats_endpoint' );

function cats_endpoint($query ){
  if ( !is_admin() && $query->is_main_query() ) {
    if ($query->query_vars['post_type'] == 'cats'){
      header('Content-Type: application/json');
      $cats = array('Hobbes', 'Simba', 'Grumpy Cat');
      echo json_encode($cats);
      exit;
     }
  }
}

keep in mind that pre_get_posts runs for every page request. Also, you might need to register the cats post_type (if it doesn't work out of the box).

  • ^ this is the same as my solution but much cleaner. (though I would still use the parse_request hook instead of pre_get_posts) – Eric_WVGG Feb 18 '15 at 21:59
  • Cats isn't a post type. I want to create these hard coded URLs on the fly – envysea Feb 18 '15 at 22:01
  • @envysea not sure you'll have to. But define "on the fly". why do you integrate it inside your WP theme at all? why not specify a custom php script that receive the parameter and output what you need? – pixeline Feb 18 '15 at 22:05
  • On the fly, meaning hardcoded – envysea Feb 18 '15 at 22:06
0

I’m sure there are better ways to do it, but I’d add this to functions.php…

add_action('parse_request', 'json_cats');
function json_cats() {
  if(!empty($_SERVER['REQUEST_URI']) || isset($_SERVER['argv'])) {
    $urlvars = explode('/', $_SERVER['REQUEST_URI']);
    if( ( isset($_SERVER['argv']) && count($_SERVER['argv']) > 1 && $_SERVER['argv'][1] == 'cats' ) || ( count($urlvars) > 1 && $urlvars[1] == 'cats' ) ) {
      header('Content-Type: application/json');
      $cats = array('Hobbes', 'Simba', 'Grumpy Cat');
      echo json_encode($cats);
    }
  }
}
0

Heres another solution

<?php

//Configruations
$endpoints = array(
    '/'     => array( 'homepage', '404 Not found' ),
    'cats'  => array( 'Hobbes', 'Simba', 'Grumpy Cat' ),
    'dogs'  => array( 'Hobbes Dog', 'Simba Dog', 'Grumpy Dog' ),
    'birds' => array( 'Hobbes Bird', 'Simba Bird', 'Grumpy Bird' ),
);

//Parsing Request :: Take the last element from endpoint /abc/def/xyz
$endpoint = isset( $_SERVER['REQUEST_URI'] ) ? strtolower( $_SERVER['REQUEST_URI'] ) : "/";
$endpoint = parse_url( $endpoint, PHP_URL_PATH );
$endpoint = array_filter(explode("/", $endpoint));
$endpoint = array_pop($endpoint);
$endpoint = array_key_exists($endpoint, $endpoints) ? $endpoint : "/";

//Print Response
header('Content-Type: application/json');
echo json_encode($endpoints[$endpoint], JSON_PRETTY_PRINT);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.