2

I would like to have a separate url for each of my product variations. For that I would need to create a rewrite rule, that will work on the product URLs

Ie, my product URL is https://example.com/product/tshirt/

The product has two variations - size and color. I would like to be able to access the product on url like https://example.com/product/tshirt/size-s-color-black

This clearly has something to do with the add_rewrite_rule function. I don't know how to build the url structure to be able to access the variations part of url

function custom_rewrite_basic() {
  add_rewrite_rule('^product/([0-9]+)/([0-9]+)?', 'index.php?page_id=$matches[1]&variations=$matches[2]', 'top');
}
add_action('init', 'custom_rewrite_basic');

Anyone?

2 Answers 2

6

Ok, for anyone looking for this, here's a complete plugin that I came up with, that solves the issue:

<?php
/*
Plugin Name: WooCommerce Variations URL
Description: Adds support for variation-specific URL's for WooCommerce product variations
Author: Václav Greif
Version: 1.0
Author URI: https://wp-programator.cz
*/

namespace WCVariationsUrl;

final class Init
{
    /**
     * Call this method to get singleton
     *
     * @return Init
     */
    public static function Instance()
    {
        static $inst = null;
        if ($inst === null) {
            $inst = new Init();
        }
        return $inst;
    }

    /**
     * Private ctor so nobody else can instance it
     *
     */
    private function __construct()
    {
        $this->add_actions();
    }

    /**
     * Add actions
     */
    function add_actions() {
        add_filter('woocommerce_dropdown_variation_attribute_options_args',array($this,'variation_dropdown_args'));
        add_action('init', array($this,'add_rewrite_rules'));
        add_action('wp_head', array($this,'add_js_to_head'));

    }

    function variation_dropdown_args($args) {
        // Get the WooCommerce atts
        $attributes =  wc_get_attribute_taxonomies();
        $atts = [];
        foreach ($attributes as $attribute) {
            $atts[] = $attribute->attribute_name;
        }

        // Get the variations part of URL
        $url_string = get_query_var('variation');

        if ($url_string) {
            $array = [];
            preg_replace_callback(
                "/(\w++)(?>-(\w+-?(?(?!" . implode("|", $atts) . ")(?-1))*))/",
                function($matches) use (&$array) {
                    $array[$matches[1]] = rtrim($matches[2], '-');
                },
                $url_string
            );

            if (!empty($array)) {
                $attribute_key = str_replace('pa_','',$args['attribute']);

                if (array_key_exists($attribute_key,$array)) {
                    $args['selected'] = $array[$attribute_key];
                }
            }
        }

        return $args;

    }

    function add_rewrite_rules() {
        add_rewrite_rule('^product/([^/]*)/([^/]*)/?','index.php?product=$matches[1]&variation=$matches[2]','top');
        add_rewrite_tag('%variation%', '([^&]+)');
    }

    function add_js_to_head() {
        if (!function_exists('is_product') || !is_product())
            return;

        global $post;
        ?>

        <script type="text/javascript">
            var url = '<?php echo get_permalink($post->ID);?>';
            jQuery( document ).ready(function($) {
                setTimeout(
                    function() {
                        $('table.variations select').on('change',function() {
                            var attributes = [];
                            var allAttributesSet = true;
                            $('table.variations select').each(function() {
                                var value = $(this).val();
                                if (value) {
                                    attributes.push({
                                        id: $(this).attr('name'),
                                        value: value
                                    });
                                } else {
                                    allAttributesSet = false;
                                }
                            });

                            if (allAttributesSet) {
                                $.each(attributes,function(key, val) {
                                    var attributeSlug = val.id.replace('attribute_pa_','');
                                    url = url + attributeSlug + '-' + val.value
                                    if($(this)[0] !== $(attributes).last()[0]) {
                                        url = url + '-';
                                    }
                                });
                                window.location.replace(url);
                            }
                        });

                    }, 1000
                )
            });
        </script>
    <?php }
}

Init::Instance();
3
  • I tried this but it keeps redirecting me back to example example.com/product/tshirt ... even if I type in the browser directly index.php?product=tshirt it still redirects me, do you have any idea why? Aug 31, 2018 at 23:12
  • @IlianAndreev Did you try flushing the permalinks? Sep 5, 2018 at 20:17
  • This will only work if the permalink pattern is not changed. Otherwise the regex won't match. Oct 19, 2020 at 20:13
0

My intention: On change reload page, pass the ID as URL?attribute so i can get it on next page as GET. I had simmilar intention. But I wanted to pass this into the URL as an better attribute. The advice here did not really suited my intention so I a bit modified it for my purpose. The shop I needed this was really using intentionally and specifically only single variations, no 2-3 mixed together.

<script>
        jQuery(function($){
            setTimeout( function(){
                $( ".single_variation_wrap" ).on( "show_variation", function ( event, variation ) {
                    //alert( variation.variation_id );
                    console.log( variation.variation_id );


                    var url = '<?php echo get_permalink($post->ID);?>';
                    //$('input.variation_id').change( function(){


                    console.log('You just selected variation #' + variation.variation_id);
                    var attributes = [];
                    var allAttributesSet = true;
                    $('table.variations select').each(function() {
                        var value = $(this).val();
                        if (value) {
                            attributes.push({
                                id: $(this).attr('name'),
                                value: value
                            });
                        } else {
                            allAttributesSet = false;
                        }
                    });
                    if (allAttributesSet) {
                        $.each(attributes,function(key, val) {
                            var attributeSlug = val.id.replace('attribute_pa_','');
                            url = url +'?variation_id='+ variation.variation_id +'&'+attributeSlug+'=' + val.value;
                        });
                        console.log('Relocating #' + variation.variation_id);
                        //window.location.replace(url);
                        window.location.href = url;
                    }

                } );
            }, 400 );

        });
    </script>

Not sure if this is the best and good approach, but this is what is working for me, and it even keeps the selected values in Variation dropdown since it is only single one.

I am calling the script above in this action:

add_action( 'woocommerce_before_add_to_cart_quantity', 'destone_variation_modification' );

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.