0

I have registed a new rest route to existing database table called wpso_messages. I was able to create a get_callback function that fetches all the data from the database.

I want to add a POST request functionality, that allows me to introduce new data rows to the wpso_messages table through the rest api. Any advise how to accomplish that?

below is my code for registering the rest route and get_callback:

<?php



function get_wp_query() {
    global $wpdb;
    $rows = $wpdb->get_results("SELECT * FROM wpso_messages");
    foreach( $rows as $index => $row ) {
    
           $user_from_id = $rows[$index]->user_from; 
           $user_to_id = $rows[$index]->user_to; 
           $rows[$index]->username_from = get_user_by('id', $user_from_id)->display_name;
           $rows[$index]->username_to = get_user_by('id', $user_to_id)->display_name;
    }
    return $rows;
};
add_action( 'rest_api_init', function () {
    register_rest_route( 'wp/v2', 'messages', array(
        'methods' => 'GET',
        'callback' => 'get_wp_query'
        ) );
    } );

For the POST request, below is an example of an input json to create new entry in the wpso_messages table:

{"user_from": "82", "user_to": "1", "message": "Iam interested to bid", "listing_id": "22775"}

Final Solution based on Sally C.J answer: Note that it includes few checks such as that user ids are valid and that the key values in the json body are in the SQL database columns.

#GET request function

function get_wp_query() {
    global $wpdb;
    $rows = $wpdb->get_results("SELECT * FROM wpso_messages");
    foreach( $rows as $index => $row ) {
    
           $user_from_id = $rows[$index]->user_from; 
           $user_to_id = $rows[$index]->user_to; 
           $rows[$index]->username_from = get_user_by('id', $user_from_id)->display_name;
           $rows[$index]->username_to = get_user_by('id', $user_to_id)->display_name;
           $rows[$index]->author= array('Welcome'=>'Home');
    }
    return $rows;
};

#POST request arguments validation

function my_create_item_args() {
    return array(
        'user_from' => array(
            'required'          => true,
            'validate_callback' => function ( $param ) {
                return is_numeric( $param );
            },
        ),
        'user_to' => array(
            'required'          => true,
            'validate_callback' => function ( $param ) {
                return is_numeric( $param );
            },
        ),
        'message'   => array(
            'required'          => true,
            'sanitize_callback' => function ( $param ) {
                // this allows basic HTML tags like <strong> and <em>
                #return wp_filter_kses( $param );

                // this allows line breaks, but strips all HTML tags
                return sanitize_textarea_field( $param );
            },
        ),
        'listing_id' => array(
            'required'          => true,
            'validate_callback' => function ( $param ) {
                return is_numeric( $param );
            },
        ),
        ''
        // ... other args.
    );
}

#Post request function
function post_function( WP_REST_Request $request ) {



    #getting json body
    $body = $request->get_json_params();
    
    #checking if there is an invalid entry otherwise it wont be processed (example unseen)
    $valid_entries=["user_from","message","user_to","listing_id","seen"];
    foreach( $body as $keysx1=>$valuesx1 ) {
        if (in_array($keysx1, $valid_entries, true)) {
        
        } else {
        return new WP_Error( 'invalid entry', __($keysx1 . ' is an invalid entry'), array( 'status' => 400 ) );
        }
    }
    
    #######Checking users
    $users = get_users();
    foreach( $users as $user ) {
        // get user names from the object and add them to the array
        $useridlist[] = $user->id;
    }
    $userfromx1=$body['user_from'];
    $usertox1=$body['user_to'];
    #Checking sender and reciever are not same
    if ($userfromx1!=$usertox1) {
        
        } else {
        return new WP_Error( 'invalid message', __('Sender and reciever cant be same'), array( 'status' => 400 ) );
        }
    #Checking userto and userfrom are in users list
    if (in_array($userfromx1, $useridlist, true)) {
        
        } else {
        return new WP_Error( 'invalid user id', __('user id specified in user_from is invalid'), array( 'status' => 400 ) );
        }
    
    if (in_array($usertox1, $useridlist, true)) {
        
        } else {
        return new WP_Error( 'invalid user id', __('user id specified in user_to is invalid'), array( 'status' => 400 ) );
        }
    
    global $wpdb;
    $body["created_at"] = current_time('mysql');
    $wpdb->insert('wpso_messages', $body);  
    return $body;

}

add_action( 'rest_api_init', function () {
    register_rest_route( 'my-plugin/v1', 'messages', array(array(
        'methods' => 'GET',
        'callback' => 'get_wp_query'
        ),
        array(
        'methods'  => 'POST',
        'callback' => 'post_function',
        'args'                => my_create_item_args(),
        'permission_callback' => function () {
        // This is just an example of checking the user's permissions..
        return current_user_can( 'edit_posts' );
    },)));
    });

1 Answer 1

1

I want to add a POST request functionality, that allows me to introduce new data rows to the wpso_messages table through the rest api. Any advise how to accomplish that?

Yes, and note that I wrote this based on the official REST API handbook and the core endpoints.

So from the "Routes and Endpoints → Routes vs Endpoints" section — note the part I highlighted:

A route is the “name” you use to access endpoints, used in the URL. A route can have multiple endpoints associated with it, and which is used depends on the HTTP verb.

Where "HTTP verb" here is essentially a HTTP request method like GET or POST.

So for example, the core /wp/v2/posts route has 2 endpoints — one with a GET method (for retrieving posts) and the other with a POST method (for creating a post).

Therefore, you could follow the same approach, i.e. add an endpoint to your "messages" route, possibly with the POST method, like so:

// I intentionally used my-plugin as the vendor name, and not "wp". See the
// "Additional Notes" at the bottom in my answer.
register_rest_route( 'my-plugin/v2', 'messages', array(
    // Endpoint 1 - list items.
    array(
        'methods'  => 'GET',
        'callback' => 'get_wp_query',
        // ... other args.
    ),

    // Endpoint 2 - create items.
    array(
        'methods'  => 'POST',
        'callback' => 'my_create_item',
        // ... other args.
    )
) );

function my_create_item( WP_REST_Request $request ) {
    // your code here...
}

The actual code that performs the new row/data insertion will depend entirely on you, but you would want to use wpdb::insert().

Additional Notes

  1. A route's namespace is composed of <vendor name>/<version>, and you should use your own vendor name, e.g. my-plugin, and not wp. So my-plugin/v2 is good, but wp/v2 shouldn't be used.

    Because the "Routes and Endpoints → Namespaces" section stated:

    Do not place anything into the wp namespace unless you are making endpoints with the intention of merging them into core.

  2. Remember to always set a permission callback for your endpoints.

    See "Routes and Endpoints → Permissions Callback" and "Adding Custom Endpoints → Permissions Callback" for more details, but for REST API routes that are intended to be public, one can use __return_true() as the permission callback, i.e. 'permission_callback' => '__return_true'.

  3. You would also want to register your endpoint arguments using the args key, and set a validate and sanitize callback which will validate/sanitize the arguments. E.g.

    • The args for Endpoint 2 above:

      // Endpoint 2 - create items.
      array(
          'methods'             => 'POST',
          'callback'            => 'my_create_item',
          'permission_callback' => function () {
              // This is just an example of checking the user's permissions..
              return current_user_can( 'edit_posts' );
          },
          'args'                => my_create_item_args(),
      )
      
    • The my_create_item_args() function:

      function my_create_item_args() {
          return array(
              'user_from' => array(
                  'required'          => true,
                  'validate_callback' => function ( $param ) {
                      return is_numeric( $param );
                  },
              ),
              'message'   => array(
                  'required'          => true,
                  'sanitize_callback' => function ( $param ) {
                      // this allows basic HTML tags like <strong> and <em>
                      return wp_filter_kses( $param );
      
                      // this allows line breaks, but strips all HTML tags
      //              return sanitize_textarea_field( $param );
                  },
              ),
              // ... other args.
          );
      }
      
6
  • Thanks for your feedback. I have added updated coding that i placed in my functions.php file in the original question. i get the following error. Any thoughts?:{'code': 'rest_no_route', 'message': 'No route was found matching the URL and request method.', 'data': {'status': 404}}
    – The Oracle
    Feb 27 at 17:11
  • 1
    That's because your POST endpoint was not registered, and it's because when supplying two or more endpoints, the 3rd parameter must be an array of endpoints, e.g. register_rest_route( 'foo/v1', '/bar', array( array( 'methods' => 'GET', ... ), array( 'methods' => 'POST', ... ) ) ) - note the endpoints are nested in an array, but yours were not. And please apply other advices in my answer, e.g. use your own namespace like my-plugin/v1 and not wp/v2, and always set a permission_callback (even for a public endpoint).
    – Sally CJ
    Feb 28 at 8:39
  • OK thanks. I'll give it a go.
    – The Oracle
    Feb 28 at 12:13
  • I have edited the code in the original question. All seems to work fine, except when i add the wpdb::insert into the post_function (its hashed in the above code), i get an error. Is there anything wrong with the wpdb::insert statement? the echo $body code line yields the following response: {"user_from":10,"message":"hello world"}
    – The Oracle
    Feb 28 at 18:01
  • Also, is there a method to validate callback that userid entered in user_from actually exists? Or I would have to code it out manually with an if statement. Much appreciated.
    – The Oracle
    Feb 28 at 22:06

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.