Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WordPress map custom url to a function

Tags:

php

wordpress

I am trying to add a custom URL structure to a WordPress based website.

for example:

example.com/machines             //list all machines in a table

example.com/machines?some=params //list filtered machines in a table

example.com/machines/1           //show single machine

The data will come from an external api i have already developed, via curl.

I cannot import the data into a custom post type as it is normalized over many tables, the business logic is complicated and the api is used by other devices anyway.

I have looked at the docs for add_rewrite_rule, but the second parameter has me stumped:

$redirect
(string) (required) The URL you would like to actually fetch

Well I don't have a url to fetch, I want to run a function, that will act as a simple router - take the url parts, call the external api and return a template with the correct data.

Calling the API will be simple, but how i actually route the url to the function, and how I then load a template (utilizing existing WordPress header.php and footer.php) has me stumped.

like image 769
Steve Avatar asked Aug 11 '16 16:08

Steve


2 Answers

After much googling and reading a few good resources, I have found the solution.

Step 1: Use add_rewrite_endpoint to create a base url that will be mapped to a query variable:

add_action( 'init', function(){
    add_rewrite_endpoint( 'machines', EP_ROOT );
} );

Step 2: Visit the permalinks settings page and click "Save Changes" to flush the rewrite rules.

Step 3: Hook into the action 'template_redirect' to actually do something when the url is hit:

add_action( 'template_redirect', function() {
    if ( $machinesUrl = get_query_var( 'machines' ) ) {
        // var_dump($machinesUrl, $_GET);
        // $machinesURl contains the url part after example.com/machines
        // e.g. if url is example.com/machines/some/thing/else
        // then $machinesUrl == 'some/thing/else'
        // and params can be retrieved via $_GET

        // after parsing url and calling api, it's just a matter of loading a template:
        locate_template( 'singe-machine.php', TRUE, TRUE );

        // then stop processing
        die();
    }
});

Step 4: The only other thing to do is handle a hit to a url with no further parts to it e.g. example.com/machines. It turns out that at some point within WordPress's guts, the empty string gets evaluated to false and thus skipped, so the final step is to hook into the filter 'request' and set a default value:

add_filter( 'request', function( $vars = [] ) {
    if ( isset( $vars['machines'] ) && empty( $vars['machines'] ) ) {
        $vars['machines'] = 'default';
    }
    return $vars;
});

This can easily be improved by wrapping it all in a class(es). The url parsing and template loading logic can be passed to a basic router, even a rudimentary MVC setup, loading routes from a file etc, but the above is the starting point.

like image 68
Steve Avatar answered Oct 10 '22 11:10

Steve


A simplier solution is to just create a new template redirect.

So assuming you loading example.com/custom-url

/**
 * Process the requests that comes to custom url.
 */
function process_request() {
    // Check if we're on the correct url
    global $wp;
    $current_slug = add_query_arg( array(), $wp->request );
    if($current_slug !== 'custom-url') {
        return false;
    }

    // Check if it's a valid request.
    $nonce = filter_input(INPUT_GET, '_wpnonce', FILTER_SANITIZE_STRING);
    if ( ! wp_verify_nonce( $nonce,  'NONCE_KEY')) {
        die( __( 'Security check', 'textdomain' ) );
    }

    // Do your stuff here
    

    //
    die('Process completed' );
}
add_action( 'template_redirect', 'process_request', 0);
like image 42
Paul Sheldrake Avatar answered Oct 10 '22 13:10

Paul Sheldrake