skip to Main Content

It’s been a while since I’ve worked with AJAX and WordPress. The way I used to do things was pretty straight forward and using the function name where the function is created within functions.php then I add action using :

add_action('wp_ajax_nopriv_myfunction', 'myfunction');
add_action('wp_ajax_myfunction', 'myfunction');

I’m drawing a blank here as now I structure my PHP code a little different and my function is no longer located in my functions.php file. It’s located within a class named:

ds_calculators

$.ajax({
    type: 'POST',
    url: my_ajax.ajaxurl,
    data: { action : 'ds_calculators::myfunction', },
    success: function(data, textStatus, XMLHttpRequest)
            {
                //jQuery('#table_listing').html(''); // empty an element
                jQuery('.changer').html(data); // put our list of links into it
            },
        error: function(XMLHttpRequest, textStatus, errorThrown)
        {
            if (typeof console === "undefined")
            {
                console = {
                    log: function() {},
                    debug: function() {},
                };
            }
            if (XMLHttpRequest.status == 404)
            {
                console.log('Element not found.');
            }
            else
            {
                console.log('Error: ' + errorThrown);
            }
        }
    });

I now add action like this

add_action('wp_ajax_nopriv_myFunction', 'ds_calculators::myFunction');
add_action('wp_ajax_myFunction', 'ds_calculators::myFunction');

For the above I’m hitting a 400(Bad Request). How does one include the function name when that function is within a class? Is that my issue or is something else my issue?

3

Answers


  1. You don’t need to specify class reference on JS side:

        $.ajax({
                  url: window.ajaxurl,
                  method: "post",
                  data: {
                    action: "myfunction",
                  },
                })
    ...
    

    In php template, if your function is in class, you need to specify it with reference to its instance ($this).

    add_action( 'wp_ajax_myfunction', array($this, 'myfunction') );
    add_action('wp_ajax_nopriv_myfunction', array($this, 'myfunction') );
    

    if (myfucntion) method is static, then you’ll use the scope operator with class name ‘ds_calculators::myfunction’

    add_action( 'wp_ajax_myfunction', 'ds_calculators::myfunction' );
    add_action('wp_ajax_nopriv_myfunction', 'ds_calculators::myfunction' );
    

    Remember wp_ajax_nopriv_{$action} fires non-authenticated Ajax actions for logged-out users.

    Login or Signup to reply.
  2. You don’t need to worry too much about namespacing if you are using the wp_ajax hooks.

    add_action( 'wp_ajax_myfunction', array($this, 'myfunction') );
    

    Is telling wordpress that when a request is sent to the default ajax url with the action set to ‘myfunction’ to pass it to the referenced callback function on resolution. the only thing to watch out for is the the wp_ajax_${your_function/action_name} must be the same as the function that it calls on resolution, and that the method in question must be public

    Here Is a great writeup on how to implement the wordpress ajax handler in a class, and where I stole this from

    namespace MyPlugin;
    
    class AjaxHandler
    {
        /**
         * Action hook used by the AJAX class.
         *
         * @var string
         */
        const ACTION = 'my_plugin';
    
        /**
         * Action argument used by the nonce validating the AJAX request.
         *
         * @var string
         */
        const NONCE = 'my-plugin-ajax';
    
        /**
         * Register the AJAX handler class with all the appropriate WordPress hooks.
         */
        public static function register()
        {
            $handler = new self();
    
            add_action('wp_ajax_' . self::ACTION, array($handler, 'handle'));
            add_action('wp_ajax_nopriv_' . self::ACTION, array($handler, 'handle'));
            add_action('wp_loaded', array($handler, 'register_script'));
        }
    
        /**
         * Handles the AJAX request for my plugin.
         */
        public function handle()
        {
            // Make sure we are getting a valid AJAX request
            check_ajax_referer(self::NONCE);
    
            // Stand back! I'm about to try... SCIENCE!
    
            die();
        }
    
        /**
         * Register our AJAX JavaScript.
         */
        public function register_script()
        {
            wp_register_script('wp_ajax', plugins_url('path/to/ajax.js', __FILE__));
            wp_localize_script('wp_ajax', 'wp_ajax_data', $this->get_ajax_data());
            wp_enqueue_script('wp_ajax');
        }
    
        /**
         * Get the AJAX data that WordPress needs to output.
         *
         * @return array
         */
        private function get_ajax_data()
        {
            return array(
                'action' => self::ACTION,
                'nonce' => wp_create_nonce(AjaxHandler::NONCE)
            );
        }
    
        /**
         * Get the comment text sent by the AJAX request.
         *
         * @return string
         */
        private function get_comment()
        {
            $comment = '';
    
            if (isset($_POST['comment'])) {
                $comment = filter_var($_POST['comment'], FILTER_SANITIZE_STRING);
            }
    
            return $comment;
        }
    
        /**
         * Get the post ID sent by the AJAX request.
         *
         * @return int
         */
        private function get_post_id()
        {
            $post_id = 0;
    
            if (isset($_POST['post_id'])) {
                $post_id = absint(filter_var($_POST['post_id'], FILTER_SANITIZE_NUMBER_INT));
            }
    
            return $post_id;
        }
    
        /**
         * Sends a JSON response with the details of the given error.
         *
         * @param WP_Error $error
         */
        private function send_error(WP_Error $error)
        {
            wp_send_json(array(
                'code' => $error->get_error_code(),
                'message' => $error->get_error_message()
            ));
        }
    }
    
    Ajax::register();
    

    If you are interested in using the REST API alongside/instead of the ajax api here is a quick class I made to go though how that would be done and showing namespacing in that setting, along with covering the basics of the wp-nonce

    <?php
    /**
     * Demo Rest API
     *
     * This file is included in my main plugin class on the
     * 'init' action hook. Then the class is instantiated
     * (aka new RestApi() is called) and the constructor for
     * this calss fires adding the rest_api_init action hook 
     * for the register_routes() method.
     *
     * @author yourname
     * @version $Id$
     * @copyright yourname, 22 February, 2023
     * @package default
     */
    
    declare(strict_types = 1);
    
    namespace TESEO;
    
    use WP_REST_Server;
    use WP_REST_Request;
    use WP_REST_Response;
    use WP_Error;
    
    /**
     * Class RestApi
     *
     * @package TESEO
     */
    class RestApi {
    
      /**
       * Define the url in a constant
       */
      const DEMO_URL = 'teseo/v1';
    
    
        /**
         * RestApi constructor.
         */
        public function __construct() {
            add_action( 'rest_api_init', array( $this, 'register_routes' ) );
            add_action( 'wp_footer', array( $this, 'TESEO_plugin_path' ) );
        }
    
    
        /**
         * Add plugin path constant to the footer
         *
         * @return void
         */
        public function TESEO_plugin_path(): void {
            ?>
            <script>
        // For use in the frontend ajax
                var my_plugin_js_vars = {
                    'plugin_url': '<?php echo plugin_dir_url( __FILE__ ); ?>'
                    'ajax_url': '<?php echo admin_url( 'admin-ajax.php' ); ?>',
                    'nonce': '<?php echo wp_create_nonce( 'npg_ajax_nonce' ); ?>',
                };
        // For use with rest you want a nonce
          var my_plugin_rest_vars = {
            'nonce': '<?php echo wp_create_nonce( 'wp_rest' ); ?>',
          };
            </script>
            <?php
        }
    
        /**
         * Register rest API routes on the 'rest_api_init' action
       *
       * This is where most of the 'namespacing' happens.
       * The default base url is '/wp-json/' with the namespace being the first part of the url.
       * So in this case the url would be '/wp-json/teseo/v1/' with the namespace being 'teseo'.
       * The version is the second part of the url, and the actual route is the third part of the url.
         */
        public function register_routes(): void {
            register_rest_route(
                'teseo/v1',
                '/dummy',
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_dummy' ),
                    'permission_callback' => array( $this, 'get_dummy_permissions_check' ),
                    'args'                => array(),
                )
            );
    
            register_rest_route(
                'teseo/v1',
                '/dummy/validate',
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array( $this, 'get_dummy_validate' ),
                    'permission_callback' => array( $this, 'get_dummy_validate_permissions_check' ),
                    'args'                => array(
                        'id' => array(
                            'required'          => true,
                            'validate_callback' => function( $param, $request, $key ) {
                                return is_numeric( $param );
                            },
                        ),
                        'nonce' => array(
                            'required'          => true,
                            'validate_callback' => function( $param, $request, $key ) {
                                return wp_verify_nonce( $param, 'wp_rest' );
                            },
                        ),
                    ),
                ),
            );
        }
    
        /**
         * Get dummy
         *
         * @param WP_REST_Request $request Request.
         *
         * @return WP_REST_Response|WP_Error
         */
        public function get_dummy( WP_REST_Request $request ): WP_REST_Response {
            $data = array(
                'message' => 'Hello World',
            );
    
            return new WP_REST_Response( $data, 200 );
        }
    
        /**
         * Get dummy permissions check
         *
         * @param WP_REST_Request $request Request.
         *
         * @return bool|WP_Error
         */
        public function get_dummy_permissions_check( WP_REST_Request $request ): bool {
            return true;
        }
    
        /**
         * Get dummy validate
         *
         * @param WP_REST_Request $request Request.
         * @return WP_REST_Response|WP_Error
         */
        public function get_dummy_validate( WP_REST_Request $request ): WP_REST_Response {
            $data = array(
                'message' => 'Hello World',
            );
    
            return new WP_REST_Response( $data, 200 );
        }
    
        /**
         * Get dummy validate permissions check
       *
       * We don't actually need to do anything here, because the permission check
       * is already done in the parameter validation. But if we wanted to, we could
       * do extra checks here for the current user, role or whatever.
         *
         * @param WP_REST_Request $request Request.
         * @return bool|WP_Error
         */
        public function get_dummy_validate_permissions_check( WP_REST_Request $request ) {
            // check if the user has the role 'administrator'
            if ( ! current_user_can( 'administrator' ) ) {
                return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permission to do this.', 'textdomain' ), array( 'status' => 401 ) );
            }
    
            // check if the users id is 1
            if ( 1 !== get_current_user_id() ) {
                return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permission to do this.', 'textdomain' ), array( 'status' => 401 ) );
            }
    
            // arbitrary check for some other condition
            return $this->validate_arbitrary_permissions_check( $request ) ? true : new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permission to do this.', 'textdomain' ), array( 'status' => 401 ) );
        }
    
        /**
         * Validate that the user making the request has made atleast 5 posts
         * in the last 30 days.
         *
         * @param WP_REST_Request $request Request.
         * @return bool|WP_Error
         */
        public function validate_arbitrary_permissions_check( WP_REST_Request $request ) {
            // get the current user
            $user = wp_get_current_user();
    
            // get the users posts
            $posts = get_posts(
                array(
                    'author'      => $user->ID,
                    'date_query'  => array(
                        array(
                            'after' => '30 days ago',
                        ),
                    ),
                    'post_type'   => 'post',
                    'post_status' => 'publish',
                )
            );
    
            // check if the user has made atleast 5 posts in the last 30 days
            if ( count( $posts ) < 5 ) {
                return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permission to do this.', 'textdomain' ), array( 'status' => 401 ) );
            }
    
            return true;
        }
    
    
    }
    
    Login or Signup to reply.
  3. You should fix your ajax call and move action argument next to the others like type, url, success instead of inside data. You are just declaring te action name and it’s not related to php class hierarchy. The actual action name it’s connected to wp_ajax_{action} and wp_ajax_nopriv_{action}. That’s the reason the action name myFunction have to be used as add_action(‘wp_ajax_myFunction‘, ‘ajax_callback’);. More about that you can read here: https://developer.wordpress.org/reference/hooks/wp_ajax_action/

    $.ajax({
        type: 'POST',
        url: my_ajax.ajaxurl,
        action: 'myfunction',
        data: {
            field1: "pass",
            field2: "some",
            field3: "data"
        },
        success: function (data, textStatus, XMLHttpRequest) {
            //jQuery('#table_listing').html(''); // empty an element
            jQuery('.changer').html(data); // put our list of links into it
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            if (typeof console === "undefined") {
                console = {
                    log: function () {},
                    debug: function () {},
                };
            }
            if (XMLHttpRequest.status == 404) {
                console.log('Element not found.');
            } else {
                console.log('Error: ' + errorThrown);
            }
        }
    });
    

    Moving to PHP side…
    You are able to call the methods inside of the class object in two ways:

    1. If it’s a normal method, you should create an instance of your class object and then use it as the array, for example:
    class ExampleClass {
        public function ajax_callback(){
            // Do staff
            // end the call with "wp_send_json"/"wp_send_json_success"/"wp_send_json_error" and die() function
            wp_send_json( "DATA" );
            die();
        }
    }
    
    $example_class_object = new ExampleClass();
    
    // Example wp_ajax actions:
    add_action('wp_ajax_nopriv_myFunction', [$example_class_object, 'ajax_callback']);
    add_action('wp_ajax_myFunction', [$example_class_object, 'ajax_callback']);
    
    1. If you don’t want to initialize an object, you can declare a method as static. That will allow you to call a method directly by name, for example:
    class StaticExmaple {
        public static function ajax_callback(){
            // Do staff
            // end the call with "wp_send_json"/"wp_send_json_success"/"wp_send_json_error" and die() function
            wp_send_json( "DATA" );
            die();
        }
    }
    
    // Example wp_ajax actions:
    add_action('wp_ajax_nopriv_myFunction', ['StaticExmaple', 'ajax_callback']);
    add_action('wp_ajax_myFunction', ['StaticExmaple', 'ajax_callback']);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search