5

I'm trying to figure out how I should call a REST API custom endpoint from the JS code of a plugin. Here is the PHP code of a sample plugin I've just written to show my issue. The filename is rest-api-sample.php:

<?php
/**
 * @link              https://www.virtualbit.it
 * @since             1.0.0
 * @package           Rest-API-Sample
 *
 * @wordpress-plugin
 * Plugin Name:       Rest API Sample
 * Plugin URI:        https://www.virtualbit.it/rest-api-sample
 * Description:       Just a code sample
 * Version:           1.0.4
 * Author:            Lucio Crusca
 * Author URI:        https://www.virtualbit.it
 * License:           GPL-2.0+
 * License URI:       http://www.gnu.org/licenses/gpl-2.0.txt
 * Text Domain:       rest-api-sample
 * Domain Path:       /languages
 */

class IESRestEndpoint 
{
  private $namespace = "ies/v1";
  public function __construct()  {
     add_action( 'rest_api_init', array($this, 'registerRoutes')); 
     add_action( 'wp_enqueue_scripts', array($this, 'enqueue_scripts'));
  }

  public function istermactive(WP_REST_Request $request)  {
    $result = true;
    return $result; // this is my controller.
  }

  public function registerRoutes()  {
    register_rest_route( $this->namespace, 
                         '/istermactive/', 
                          array('methods' => 'GET',
                                'callback' => array($this, 'istermactive')
                               )
                       );
  }

  public function enqueue_scripts() {    
    $handle = "ies-rest-api";
    $jsfileurl = plugin_dir_url( __FILE__ ) . '/ies.js';
    wp_register_script($handle, $jsfileurl, array("underscore", "backbone", "wp-api"));    
    $local_data = array('apiRoot' => get_rest_url(), "namespace" => $this->namespace);
    wp_localize_script($handle, "ies_rest", $local_data);
    wp_enqueue_script($handle);
  }  
}

$ies_endpoint = new IESRestEndpoint();

And here is the ies.js code:

(function( $ ) {
  'use strict';

  $(document).ready(function()
  {  
    wp.api.init({'versionString' : ies_rest.namespace,  'apiRoot': ies_rest.apiRoot}).done(function()
    { 
      wp.api.loadPromise.done(function () 
      {
        wp.api.namespace(ies_rest.namespace).istermactive().done(function (active)
        {
          alert(active);
        });
      });
    });    
  });

})( jQuery );

However this JS code, in WP 4.7.4/4.7.5, throws an exception in the JS console after the call to wp.api.init() and before it reaches the call to wp.api.loadPromise():

Uncaught TypeError: _.includes is not a function
    at wp-api.min.js?ver=4.7.4:1
    at Function.h.each.h.forEach (underscore-min.js?ver=4.7.4:1)
    at Object.wp.api.utils.decorateFromRoute (wp-api.min.js?ver=4.7.4:1)
    at wp-api.min.js?ver=4.7.4:1
    at Function.h.each.h.forEach (underscore-min.js?ver=4.7.4:1)
    at n.constructFromSchema (wp-api.min.js?ver=4.7.4:1)
    at n.<anonymous> (wp-api.min.js?ver=4.7.4:1)
    at n.<anonymous> (backbone.min.js?ver=1.2.3:1)
    at n.<anonymous> (underscore.min.js?ver=1.8.3:5)
    at _ (backbone.min.js?ver=1.2.3:1)

The same code, using WP 4.8beta2, seems to work at least until it reaches the wp.api.namespace() call, where I get:

Uncaught TypeError: wp.api.namespace is not a function
    at Object.<anonymous> (https://www.virtualbit.it/wp-content/plugins/rest-api-sample//ies.js?ver=4.8-beta2:10:16)
    at i (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:27449)
    at Object.add [as done] (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:27748)
    at Object.<anonymous> (https://www.virtualbit.it/wp-content/plugins/rest-api-sample//ies.js?ver=4.8-beta2:8:26)
    at i (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:27449)
    at Object.fireWith [as resolveWith] (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:28213)
    at Object.e.(anonymous function) [as resolve] (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:29192)
    at Object.<anonymous> (https://www.virtualbit.it/wp-includes/js/wp-api.min.js?ver=4.8-beta2:1:13404)
    at i (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:27449)
    at Object.fireWith [as resolveWith] (https://www.virtualbit.it/wp-includes/js/jquery/jquery.js?ver=1.12.4:2:28213)

My JS file (ies.js) is not even mentioned in the stack trace of the 4.7.x exception, while in 4.8beta2 it seems a simple syntax error, but I don't know how should I otherwise call my endpoint.

I thought it could be a WP bug (maybe a documentation bug), and I opened a bug report. It turned out it's NOT a WP bug, so this question still needs an answer, but Adam, who replied to my bug report, gave us some valuable informations.

As for the suggestion birgire gave in comments, unfortunately I have no clue about how to use the QUnit code, maybe you can walk me through that?

If you want to try this sample plugin, you can find it here, and, just in case you are interested, you can debug the JS code at my website homepage, where this sample plugin is already installed (WP version is 4.8beta2 as of time of this writing).

  • 1
    &$this is a workaround they used for PHP4 support, you should only use it if you intend to support WordPress 3.1 and below running on PHP 4.4 or older. Use $this instead – Tom J Nowell May 15 '17 at 0:40
  • thanks for the tip, I didn't know. I've edited my question and my sources. – Lucio Crusca May 15 '17 at 5:49
  • 1
    any chance this needs to be loadPromise.done() instead of loadPromise.then()? – hwl May 15 '17 at 6:57
  • 1
    To be able to do some sane debugging use uncompressed versions of libraries define('SCRIPT_DEBUG', false); – Zlatev May 15 '17 at 11:30
  • 1
    @TomJNowell I've just edited the question with the full sample plugin code – Lucio Crusca May 15 '17 at 20:43
6
+50

It looks like you are trying to send a POST request to the /istermactive endpoint, is that correct? (I think you may want to remove the trailing slash from the endpoint?)

I'm not really sure the wp-api client is the right tool for a standard ajax POST, you may want to use jQuery.ajax or use WordPress's helper wp-ajax.

The wp-api client is designed to help you when you want to interact with a collection of items or a single item (eg posts or a post) when retrieved from the WP-API. It creates Backbone models and collections by parsing the API schema. Check here for some docs (which are in need of some updates).

3

previous answer:

It appears your JS isn't loaded. You have registered the script, but haven't enqueued it. Add wp_enqueue_script after you register it. You can also use wp_localize_script to pass dependencies to your script and make variables from your php available to it.

Both above linked to codex on those functions.

  • oh yes yes. rereading now, I see I missed what you were asking when I read it earlier. – hwl May 15 '17 at 6:52
0

It seems the first error is related to Underscore.js, you should try adding underscore (and Backbone) as your script's dependency:

wp_register_script($handle, $jsfileurl, array("underscore", "backbone", "wp-api"));
  • I've tried both in 4.7.x and 4.8beta2, but it does not change anything. – Lucio Crusca May 23 '17 at 5:12
  • Eheh, I tried the obvious...have been installing the plugin and tried it: it seems to work fine if you pass the right apiRoot parameter as suggested here – y_power May 23 '17 at 6:15
  • Could you please edit your answer and share your solution with us? – Lucio Crusca May 23 '17 at 6:21
  • sure: I just added wp.api.init({'versionString' : 'ies/v1/', 'apiRoot': 'your_root_goes_here'}).done(function()... in the js file – y_power May 23 '17 at 6:29
  • Tried that also, but still no dice. I've updated my question accordingly. – Lucio Crusca May 23 '17 at 17:09
0

The solution to what I'm trying to do, as requested by the bounty description (e.g. using backbone.js), does not exist. As per Adam Silverstein (core WP developer) answer here and by private messages, I've now understood that:

  1. namespace() simply does not exist in the WP bundled Rest Api client (backbone.js) and if I want to POST to a PHP function, I have to use standard ajax calls
  2. If I use backbone, I should swap the wp.api.init and wp.api.loadPromise calls, e.g. loadPromise before init.
  3. My PHP code, if I want to use backbone.js, must define models and collections
  4. However I can also use the same client I was using back in the WP API plugin days with the current Rest API, and I get my namespace() function back.

Thanks Adam, you deserve the bounty reward.

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.