How to use add_filter and apply_filters in WooCommerce

Filters in WordPress allow you to alter content without directly altering core WordPress files, they also allow you to alter dynamically generated content before it is shown to users.

As WooCommerce is built on top of WordPress filters are used extensively, and if you want to learn how to modify WooCommerce you’ll need to build a good understanding of actions and filters.

As we have already covered actions in another article we’ll take a look at filters in this article, we’ll firstly look at a contrived example to increase our understanding and the take a look at a real example from the WordPress codebase.

A Contrived Example

Let’s imagine someone has written a theme for film lovers and they have included this line of code

echo "My Favorite Films: " . apply_filters( 'my_favorite_films', 'Star Wars, The Little Mermaid');

As we can see, the theme’s coder has included a line that lists the website owner’s favorite films, but rather than hard coding the film names they have used the apply_filters function which makes it easier for us to change the list of films. As it stands, the code above would output this line

Let’s take a look at the code we’d need to write to change the list.

function add_an_extra_film( $films ) {
    return $films . ", Dirty Dancing";
}

add_filter( 'my_favorite_films', 'add_an_extra_film', 1 );

Here’s what’s happening –

  • We define a function named add_an_extra_film, the function takes a value and appends the string “, Dirty Dancing” to it.
  • We call add_filter and pass the tag “my_favorite_films”,  along with the name of the function that we defined at the start of the code. We also pass the value 1 for the priority value, we’ll discuss this in more detail later.

Once the code has been run then any time that the apply_filters function is called with the tag “my_favorite_films”, then our add_an_extra_film function will be called.

A good way to think about this to imagine a list of functions that is added to each time the add_filter function is called. All of the functions in the list are then run when the apply_filters function is called. WordPress uses the tag values passed to apply_filter and add_filter to keep track of which functions belong to which filter.  Wordpress uses the priority values passed in the add_filter to decide what order to run the functions in.

To further understand what happens when apply_filters is called, let’s imagine that we add our “add_an_extra_film” function to the list of functions multiple times

add_filter( 'my_favorite_films', 'add_an_extra_film', 1 );
add_filter( 'my_favorite_films', 'add_an_extra_film', 1 );
add_filter( 'my_favorite_films', 'add_an_extra_film', 1 );

If we ran the code above then the function would be called three times when the apply_filters function is called, each time the function is called the value that is passed to the function  will be the return value from the previous function, here’s a break down of the inputs and outputs of each function call that would result if we ran the code above

The add_an_extra_film function is run for the first time. The value passed into the function is “Star Wars, The Little Mermaid” (which is the value supplied when we make the apply_filter call), the function returns “Star Wars, The Little Mermaid, Dirty Dancing”.

The add_an_extra_film function is run for the second time. The value passed into the function is “Star Wars, The Little Mermaid” (which is the return value of the previous call), the function returns “Star Wars, The Little Mermaid, Dirty Dancing, Dirty Dancing”

The add_an_extra_film function is run for the third time. The value passed into the function is “Star Wars, The Little Mermaid, Dirty Dancing, Dirty Dancing” (which is the return value of the previous call), the function returns “Star Wars, The Little Mermaid, Dirty Dancing, Dirty Dancing, Dirty Dancing”

This is an extract from the book “Learning WooCommerce Development By Example”, click here to find out more.

Priority

In the example above we pass the priority 1 on each function so we don’t know in what order the functions will be called (and it doesn’t matter as all the functions make the same change to the initial value),

Let’s imagine that the following code is run before we add our functions

function add_jaws_to_list_of_films( $films ) {
    return $films . ", Jaws";
}

add_filter( 'my_favorite_films', 'add_jaws_to_list_of_films', 15 );

Because the add_jaws_to_list_of_films function is given a priority of 15 when we add it with the add_filter then it will fire after all of the functions in the previous example, and Jaws will be the last film added to the list. If we wanted Jaws to be added before the values returned from our functions then we would need to alter the priority value in the add_filter calls to something above 15.

Removing Filters

It is possible to remove filters, so if we wanted to remove Jaws from the list completely we could run the following code

remove_filter( 'my_favorite_films', 'add_jaws_to_list_of_films', 15);

Note that if a priority value was supplied when the function was added the same priority value must also be supplied in order to successfully remove the function from the filter list.

Passing Arguments

In all the examples we have looked at so far, we have only passed a single argument to the apply_filter call, which is the initial value of the favorite films list. Each function that we link to the filter is then able to access and change this value.

It is possible to pass additional arguments to apply_filter function, let’s take a look at an example below

$initial_film_lists = array(
    "Hipster" => "La Regle du jeu, Portrait of a Lady on Fire",
    "Action" => "Point Break, The Rock"
);

echo "My Favorite Films: " . apply_filters( 'my_favorite_films', 'Star Wars, The Little Mermaid', $initial_film_lists );

Here, we have added an array of initial film lists, the thinking behind this is if the people using the code are running a particular type of film blog say a “hipster” or an “action” film blog, then they could use one of the initial lists to add their favorite films to. Let’s have look at an example of how they could do this

function add_an_extra_action_film( $films, $alternateLists ) {
    return $alternateLists["Action"] . ", The Sting";
}

add_filter( 'my_favorite_films', 'add_an_extra_action_film', 1, 2);

This would then show the following

The code begins by defining the function add_an_extra_action_film, the function looks up the “Action” entry in the $alternateLists and then appends it to a string that adds an extra film name to the list. We then add the add_an_extra_action_film function to the list of functions via the add_filter function.

Note that when we call add_filter we pass an extra argument with the value 2 at the end of the argument list

add_filter( 'my_favorite_films', 'add_an_extra_action_film', 1, 2);

The number two tells the add_filter  function that we wish to pass two arguments to the function defined in the second argument passed to add_filter, if we omit this argument then WordPress assumes that the function will only require a single argument. If we ran the code above with the final argument omitted then we would receive an error

It’s worth noting that even though we have passed two arguments via the apply_filters function it is still possible to add a function that only requires one argument via the add_filter function, we will only get an error if we add a function that requires more than one argument but we don’t specify in the call that more than one argument is required.

function add_an_extra_action_film( $films, $alternateLists ) {
    return $alternateLists["Action"] . ", The Sting";
}

//the add_an_extra_action_film function requires two arguments so we must specify that in the add_filter arguments
add_filter( 'my_favorite_films', 'add_an_extra_action_film', 1, 2);

function add_hudson_hawk_to_list_of_films( $films) {
    return $films . ", Hudson Hawk";
}

//the add_hudson_hawk_to_list_of_films function only requires one argument so we don't need to specify the number of arguments
add_filter( 'my_favorite_films', 'add_hudson_hawk_to_list_of_films', 1);

An Example from WooCommerce

Now we have looked at a contrived example, let’s look at an example from the WooCommerce codebase

global $product;

echo apply_filters(
    'woocommerce_loop_add_to_cart_link', // WPCS: XSS ok.
    sprintf(
        '<a href="%s" data-quantity="%s" class="%s" %s>%s</a>',
        esc_url( $product->add_to_cart_url() ),
        esc_attr( isset( $args['quantity'] ) ? $args['quantity'] : 1 ),
        esc_attr( isset( $args['class'] ) ? $args['class'] : 'button' ),
        isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : '',
        esc_html( $product->add_to_cart_text() )
    ),
    $product,
    $args
);

The code above can be found in https://github.com/woocommerce/woocommerce/blob/master/templates/loop/add-to-cart.php

The code outputs the HTML mark-up that shows the “Add to Cart” button on the shop page

As you can see from the code, the HTML mark-up for the button is passed as the value argument of the apply_filters function, so if no functions are added to the filter then it is this code that will be displayed. As well as the value argument, additional arguments are supplied that provide details of the product the button is been created for and an array containing further data.

Now let’s look at an example of how the filter can be used to change the button

add_filter( 'woocommerce_loop_add_to_cart_link', 'hwn_replace_add_to_cart_button', 10, 2 );
function hwn_replace_add_to_cart_button( $button, $product  ) {
    $button_text = __("View Product", "woocommerce");
    $button = '<a class="button" href="' . $product->get_permalink() . '">' . $button_text . '</a>';
    return $button;
}

As you can see from the code, a function named hwn_replace_add_to_cart_button is created, the function creates the HTML mark-up for a button that redirects to the product details page rather than adding the product to the cart.

The code uses the $product argument to get the URL for the product details page, as this argument is used the accepted argument parameter of the add_filter function is set to 2.

If we wanted the function to completely remove the add to cart button then we could dispense with the $product argument and remove the accepted argument parameter of the add_filter function, as in the example below.

//as the hwn_remove_add_to_cart_button function returns a blank string we do not
//need to pass any of the additional arguments to the function, so we do not need
//to supply a value for the accepted argument parameter 
add_filter( 'woocommerce_loop_add_to_cart_link', 'hwn_remove_add_to_cart_button', 10);

//as we are returning a blank string we do not need any of the extra arguments so 
function hwn_remove_add_to_cart_button() {
    return "";
}

Final Thoughts

The add_filter and apply_filter functions allow us to alter content before it is delivered to users, the functions can be a little hard to understand but hopefully, this article has helped you to understand how the functions work and when they may be useful.

Learn the basics of WooCommerce Development, in a friendly and accessible way
Click here for more details about the "Learning WooCommerce Development By Example" book

If you have any questions or queries about the article then please don’t hesitate to let me know in the comments.

2 thoughts on “How to use add_filter and apply_filters in WooCommerce”

  1. In your last example, the code has to be:
    add_filter( ‘woocommerce_loop_add_to_cart_link’, ‘hwn_remove_add_to_cart_button’, 10, 2);
    function hwn_remove_add_to_cart_button( $button, $product ) {
    return “”;
    }
    Otherwise you get:
    Fatal error: Uncaught ArgumentCountError: Too few arguments to function hwn_remove_add_to_cart_button(), 1 passed in /Users/xxx/Flywheel Sites/xxx/app/public/wp-includes/class-wp-hook.php on line 309 and exactly 2 expected in /Users/xxx/Flywheel Sites/xxx/app/public/wp-content/plugins/woocommerce-snippets/woocommerce-snippets.php on line 19

    Reply
    • Hi Alain, thanks so much for spotting that. The code was different to what I’d exaplained in the comment and the hwn_remove_add_to_cart_button should have had no arguments passed to it. Your solution was also an excellent fix. I’ve updated the code in the post now, so, thanks to you, it should be fixed for future readers 🙂

      Reply

Leave a Comment