Link product attribute value to a URL – woocommerce

All we need is an easy explanation of the problem, so here it is.

I would like to add a local product attribute value that will have a link which points to a particular ‘href’ value. I followed this blog and tried to add via the markup option mentioned at the end of the blog, the value turns into an anchor tag link but the href value is not taken properly.
https://nicolamustone.blog/2016/03/11/make-product-attributes-linkable/

I added this snippet from the blog in my functions.php

/**
 * Register term fields
 */
add_action( 'init', 'register_attributes_url_meta' );
function register_attributes_url_meta() {
        $attributes = wc_get_attribute_taxonomies();

        foreach ( $attributes as $tax ) {
            $name = wc_attribute_taxonomy_name( $tax->attribute_name );

            add_action( $name . '_add_form_fields', 'add_attribute_url_meta_field' );
            add_action( $name . '_edit_form_fields', 'edit_attribute_url_meta_field', 10 );
            add_action( 'edit_' . $name, 'save_attribute_url' );
            add_action( 'create_' . $name, 'save_attribute_url' );
        }
}

/**
 * Add term fields form
 */
function add_attribute_url_meta_field() {

    wp_nonce_field( basename( __FILE__ ), 'attrbute_url_meta_nonce' );
    ?>

    <div class="form-field">
        <label for="attribute_url"><?php _e( 'URL', 'domain' ); ?></label>
        <input type="url" name="attribute_url" id="attribute_url" value="" />
    </div>
    <?php
}

/**
 * Edit term fields form
 */
function edit_attribute_url_meta_field( $term ) {

    $url = get_term_meta( $term->term_id, 'attribute_url', true );
    wp_nonce_field( basename( __FILE__ ), 'attrbute_url_meta_nonce' );
    ?>
    <tr class="form-field">
        <th scope="row" valign="top"><label for="attribute_url"><?php _e( 'URL', 'domain' ); ?></label></th>
        <td>
            <input type="url" name="attribute_url" id="attribute_url" value="<?php echo esc_url( $url ); ?>" />
        </td>
    </tr>
    <?php
}

/**
 * Save term fields
 */
function save_attribute_url( $term_id ) {
    if ( ! isset( $_POST['attribute_url'] ) || ! wp_verify_nonce( $_POST['attrbute_url_meta_nonce'], basename( __FILE__ ) ) ) {
        return;
    }

    $old_url = get_term_meta( $term_id, 'attribute_url', true );
    $new_url = esc_url( $_POST['attribute_url'] );


    if ( ! empty( $old_url ) && $new_url === '' ) {
        delete_term_meta( $term_id, 'attribute_url' );
    } else if ( $old_url !== $new_url ) {
        update_term_meta( $term_id, 'attribute_url', $new_url, $old_url );
    }
}

/**
 * Show term URL
 */
add_filter( 'woocommerce_attribute', 'make_product_atts_linkable', 10, 3 );
function make_product_atts_linkable( $text, $attribute, $values ) {
    $new_values = array();
    foreach ( $values as $value ) {

        if ( $attribute['is_taxonomy'] ) {
            $term = get_term_by( 'name', $value, $attribute['name'] );
            $url = get_term_meta( $term->term_id, 'attribute_url', true );

            if ( ! empty( $url ) ) {
                $val = '<a href="' . esc_url( $url ) . '" title="' . esc_attr( $value ) . '">' . $value . '</a>';
                array_push( $new_values, $val );
            } else {
                array_push( $new_values, $value );
            }
        } else {
            $matched = preg_match_all( "/\[([^\]]+)\]\(([^)]+)\)/", $value, $matches );

            if ( $matched && count( $matches ) == 3 ) {
                $val = '<a href="' . esc_url( $matches[2][0] ) . '" title="' . esc_attr( $matches[1][0] ) . '">' . sanitize_text_field( $matches[1][0] ) . '</a>';
                array_push( $new_values, $val );
            } else {
                array_push( $new_values, $value );
            }
        }
    }

    $text = implode( ', ', $new_values );

    return $text;
}

I added the cutom attribute value as : [Displayed text](https://expected-display-url.com)

What I expected it to output:

<a href="https://expected-display-url.com">Displayed text</a> 

But what it converted instead:

<a title="Displayed text">Displayed text</a> 

Even when I tried to add a specific title value like: [Displayed text](https://expected-display-url.com "title")

It always rendered as: <a title="Displayed text">Displayed text</a>

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

With the current WooCommerce release, try this instead of the make_product_atts_linkable() function in the question:

function make_product_atts_linkable( $text, $attribute, $values ) {
    $vals = array();

    if ( $attribute->is_taxonomy() ) {
        foreach ( $attribute->get_options() as $i => $id ) {
            if ( $url = get_term_meta( $id, 'attribute_url', true ) ) {
                $vals[] = '<a href="' . esc_url( $url ) . '">' . esc_html( get_term_field( 'name', $id ) ) . '</a>';
            } else {
                $vals[] = $values[ $i ];
            }
        }
    } else {
        foreach ( $attribute->get_options() as $value ) {
            $vals[] = preg_replace_callback( '/\[([^\]]+)\]\(([^) ]+)(?: "([^"]+)"|)\)/', function ( $matches ) {
                $title = ( ! empty( $matches[3] ) ) ? ' title="' . esc_attr( $matches[3] ) . '"' : '';
                return '<a href="' . esc_url( $matches[2] ) . '"' . $title . '>' . esc_html( $matches[1] ) . '</a>';
            }, $value );
        }
    }

    return wpautop( wptexturize( implode( ', ', $vals ) ) );
    //return implode( ', ', $vals ); // Use this or the above. Up to you..
}

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

Leave a Reply