2

I am working on a website that is more of a search directory and haven't been able to find a clear answer on how to automatically create pretty links from long URL's with multiple query parameters (Wordpress).

For example, if the link looks like this: website.com/search/search-results/?address=98101,+seattle,+washington&contractor=plumber&latitude=1234&longitude=9876&filter=20&order=distance

Is there a way to make it look like this: website.com/search/search-results/98101/seattle/washington/plumber/

automatically without having to go through every link and the new pretty link will show the same page as the page with all of the query parameters?

Thanks in advance, I've been trying to figure this out all day and it's not my strong point...

Ryan
  • 31
  • 3

1 Answers1

0

Really interesting and quite technical question.

The following is not production ready. It is a working proof of concept.

I'm just pondering here. You could create a new Table (see Creating Tables with Plugins with a requested_url key and a shortened_url value. The whole system would be based on that approach.

First we creates a custom table in the database, if it doesn’t already exist. This table will be used to store our urls.

<?php

add_action( 'after_switch_theme', function () {

    global $wpdb;

    $table_name = $wpdb->prefix . 'wpso74035985';
    
    $charset_collate = $wpdb->get_charset_collate();

    $create_ddl = "CREATE TABLE IF NOT EXISTS $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        requested_url varchar(55) DEFAULT '' NOT NULL,
        shortened_url varchar(55) DEFAULT '' NOT NULL,
        PRIMARY KEY  (id)
    ) $charset_collate;";
    
    require_once ABSPATH . 'wp-admin/includes/upgrade.php';

    maybe_create_table( $table_name, $create_ddl );

} );

We could then retrieve the requested url by parsing the $_SERVER['QUERY_STRING'] and verifying the url integrity through get_query_var() as it only retrieves public query variables that are recognized by WP_Query.

We would then search the table for the requested url, and if it doesn't already exist, set a new key pair value.

<?php

add_action( 'pre_get_posts', function ( $wp_query ) {

    if ( ! is_admin() && $wp_query->is_main_query() ) {

        if ( $wp_query->is_search() ) {

            global $wp;

            parse_str( filter_input( INPUT_SERVER, 'QUERY_STRING', FILTER_SANITIZE_STRING ), $variables );

            $buffer = array();
        
            foreach ( $variables as $variable => $value ) {
        
                if ( get_query_var( $variable ) ) {
        
                    array_push( $buffer, array( $variable => $value ) );
        
                };
                
            };
        
            $buffer = array_reduce( $buffer, 'array_merge', array() );
        
            $requested_url = esc_url_raw( add_query_arg( $buffer, home_url( $wp->request ) ) );

            global $wpdb;
    
            $table_name = $wpdb->prefix . 'wpso74035985';

            $results = $wpdb->get_results(
    
                "SELECT * FROM $table_name"
                
            );
                
            $needle_key = array_search( $requested_url, array_column( $results, 'requested_url' ) );

            if ( $needle_key === false ) {

                $shortened_url = str_shuffle( base64_encode( random_bytes( 100 ) ) );

                $shortened_url = preg_replace( '/\W/s', '', $shortened_url );

                $shortened_url = substr( $shortened_url, 0, 7 );
    
                $wpdb->insert(
                    $table_name,
                    array(
                        'requested_url' => sanitize_url( $requested_url ),
                        'shortened_url' => sanitize_url( home_url( $shortened_url ) ), 
                    )
                );

            };

        };

    };

} );

There is no real science here, the $shortened_url is just a 7 bytes alpha numerical string (Why 7? Bit.ly a famous shortener is using 7 characters).

Through the template_redirect hook, we could check the shortened url against our table and redirect based on that.

<?php

add_action( 'template_redirect', function () {

    if ( is_404() ) {

        global $wp;

        $shortened_url = home_url( $wp->request );
    
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'shortener';
    
        $results = $wpdb->get_results(
    
            "SELECT * FROM $table_name"
            
        );
            
        $needle_key = array_search( $shortened_url, array_column( $results, 'shortened_url' ) );
    
        if ( $needle_key !== false ) {

            $location = $results[$needle_key]->requested_url;
    
            wp_safe_redirect( $location );
    
            exit();

        };

    };

} );

As we're checking for a 404 through is_404() first (pretty much limiting the number of empty request), you should also include a 404.php to your root.

On the front end you can access your table through

<?php

global $wpdb;
            
$table_name = $wpdb->prefix . 'wpso74035985';
        
$results = $wpdb->get_results(
        
    "SELECT * FROM $table_name"
                
);

var_dump( $results );

Now, a few thing to understand before you go to production with this, even tho we're generating a pseudo random string through random_bytes() and base64_encode() they're not actually fully randomized, so you could end up with a problem at some point where two shortened url are the same. You could do a while loop and constantly check if that shortened url already exist before using it.

You would also want to restrict the search query as much as you can to improve performance on the long term.

You might want also to add an expiry date.

amarinediary
  • 4,930
  • 4
  • 27
  • 45
  • 1
    A custom post type might also work in place of the new tables. The `post_excerpt` column might be a place to put some of that data. – O. Jones Oct 12 '22 at 14:12