100% found this document useful (2 votes)
808 views

Learning WooCommerce Development by Example

This document provides tutorials and code snippets for various WooCommerce development tasks. It includes sections on adding code snippets, using do_action and add_action hooks, creating child themes, working with attributes, variable products, local development environments, custom fields, coupons, product details, cart contents, and user details. Code examples and best practices are provided for each topic.

Uploaded by

jorge
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
808 views

Learning WooCommerce Development by Example

This document provides tutorials and code snippets for various WooCommerce development tasks. It includes sections on adding code snippets, using do_action and add_action hooks, creating child themes, working with attributes, variable products, local development environments, custom fields, coupons, product details, cart contents, and user details. Code examples and best practices are provided for each topic.

Uploaded by

jorge
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 249

Learning WooCommerce Development

By Example
How to add a Code Snippet to WooCommerce 16

What’s the best way to add a Code Snippet to WooCommerce? 16

Using the functions.php File in Your Theme 16

How it’s Done 17

Via the WordPress Admin Dashboard 18

Will the Editor Allow Me to Enter Bad Code? 19

Pros and Cons of Adding Snippets to the functions.php File in Your


Theme 19

Using the functions.php File in Your Child Theme 20

Pros and Cons of Adding Snippets to the functions.php File in Your


Child Theme 20

Via a Third Party Plugin 21

Pros and Cons of Adding Snippets via a Third Party Plugin 23

Via a Plugin we Write Ourselves 24

Pros and Cons of Adding Snippets via a Plugin We Write Ourselves 28

Final Thoughts 29

do_action and add_action in WooCommerce and WordPress 30

Changing the order the functions are called in 32

Passing arguments to functions 33

Removing functions with remove_action 34

2
Why bother with do_action and add_action couldn’t we just write stuff directly to the
page? 35

do_action and add_action in woocommerce 36

How to use add_filter and apply_filters in WooCommerce 37

A Contrived Example 37

Priority 39

Removing Filters 40

Passing Arguments 40

An Example from WooCommerce 43

Final Thoughts 46

How to Create a Storefront Child Theme 47

What are the Benefits of Creating a Storefront Child Theme 47

We then need to create a file in the directory called “style.css” 49

Activating the Child Theme 50

How to Upload Your Theme to a Remote Site 51

FTP 51

Upload your Child Theme as a Zip File 52

What Changes Can I Make with my Child Theme? 53

Style Changes 53

3
Changes via the functions.php File 54

Changes made by Altering Files 55

A Note About Enqueuing Styles 59

Is There a Way to Add Permanent CSS and Code Changes Without


Creating a Child Theme 60

How to Add Javascript to Your Child Theme 60

Understanding Attributes in Woocommerce 63

Why would you use Attributes in Woocommerce? 63

Creating Attributes in Woocommerce 63

Adding a Global Attribute 63

Adding a Custom Product Attribute 65

What’s the Point of Custom Product Attributes? 65

Filtering by Attributes in Woocommerce 66

The “Filter Products by Attribute” widget 68

Accessing Product Attributes via PHP 70

Understanding Woocommerce Variable Products 72

What are Variable Products and when should you use them? 72

How to set up a variable product 73

Categories and Attributes on a Woocommerce site 73

4
Categories and Attributes and Variable Products 74

How to create the variable products 77

Setting up a Woocommerce Development Environment with Local by


Flywheel and Visual Studio Code 81

Local by Flywheel 81

Visual Studio Code 86

Turning The Site into a Blueprint 92

Conclusion 94

How to Add a PHP Page to WooCommerce 95

Creating the PHP Page 95

Creating a WordPress/WooCommerce page that uses the PHP File 96

Testing the New Page 98

Final Thoughts 99

How to Detect Category and Tag Pages in Woocommerce 100

The Problem 100

The Solution 100

How to Generate Links To Categories and Tag Archives 102

Problem 102

Solution 102

5
Generating Links to Multiple Categories or Tags 103

Excluding Empty Categories/Tags 104

Conclusion 105

How to Add a Custom Field to a Product 106

The Problem 106

The Solution 106

Adding the Field to the Product Screen 106

Limiting the Custom Field To Specific Products 108

Showing Different Inputs 109

Validating the Custom Field 111

Adding the Gift Note to the Cart Item Data 114

Displaying the Gift Note Data in the Cart 115

Adding the Gift Note Data to an Order Line Item 117

Conclusion 119

How to Programmatically Create a Coupon 121

The Problem 121

Creating a Coupon Programmatically 121

Adding the Programmatically Created Coupon to a User’s Basket 124

The coupon_exists and cart_contains_items functions 127

6
How to Check a Products Type 130

The Problem 130

The Solution 130

Should I use get_type() or is_type() 131

Final Notes 131

How to Get A Products Details Using a Product Id 132

The Problem 132

Scenario 1 - We are on A Product Page, or a Page that Displays Lists of


Products 132

How do I Know if the $product Global Variable is not Available? 133

The $product global variable should be available on most customer


facing shop pages, you can check if the $product global variable has
been populated using the following code. 133

Scenario 2 - We only have Access to the Product Id 134

Final Notes 135

How to Access the Details of all The Products in a Users Basket 136

The Problem 136

The Solution 136

Different ways of writing the foreach statement 139

Dealing with Empty Carts 139

7
Some Example Code that Displays Cart Data 140

How to Get User Details from the Cart 142

The Problem 142

The Solution 142

How to Check If an Item Is in a User Cart 144

The Problem 144

The Solution 144

Another Solution 147

How to Add a Surcharge Based on a Custom Field 149

The Problem 149

The Solution 149

Adding Multiple Charges 151

Charging Different Fees 152

How to change Labels on the Checkout Screen 155

The Problem 155

The Solution 155

How to change Placeholders on the Checkout Screen 157

The Problem 157

8
The Solution 157

How to Change the Order of Fields on the Checkout Screen 160

The Problem 160

The Solution 161

How to Remove a Field on the Checkout Screen 165

The Problem 165

The Solution 165

How to Make a Field Required on the Checkout Screen 167

The Problem 167

The Solution 167

How to Add a Field to the Billing Address on the Checkout Screen 169

The Problem 169

The Solution 169

How to View the New Field and it’s Value on The Order Admin Screen 171

How to change a Shipping Address Field on the Checkout Screen 173

The Problem 173

The Solution 173

How to change an Address Field in both the Shipping and Billing


Address on the Checkout Screen 176

9
The Problem 176

The Solution 176

Overriding Default Fields 178

How to Remove the Option for Users to Supply a Shipping Address 181

The Problem 181

The Solution 181

Solving the Problem in Code 183

How to Add a New Field to the Checkout Screen 185

The Problem 185

The Solution 185

Validating the Field 187

Saving the Field Value 188

Showing the Field Value in the Admin Order Screen 190

How to Add a Custom Field on the My Account - Account Details Page


194

The Problem 194

The Solution 194

How to Add an Item to the Menu in the My Account Page 198

The Problem 198

10
The Solution 198

Linking the New Item to a Url 200

Linking the New Item to Custom Content 201

Making sure Wordpress Can See Our New Endpoint 205

Checklist 206

Adding the New Item in a Specific Position in the Menu 207

Altering the Icon of the New Menu Item 209

How to Remove an Item from the Menu in the My Account Page 215

The Problem 215

The Solution 215

Removing Other Items from the Menu 217

Removing Items from the Menu Without Using Code 218

How to Send Custom Emails Based on Order Details 222

The Problem 222

The Solution 222

How to Add a Field to The Product Edit Screen 226

The Problem 226

Adding the Field to the Admin Page 226

11
Saving the Field Value 228

Using the Value in Customer Facing Pages 229

Final Notes 229

How to Only Show Custom Edit Screen Fields for Certain Product Types
231

The Problem 231

Limiting the Display of the Custom Field by Product Type 231

Why Won’t this Approach Work? 233

Final Notes 235

How to Access Order Details 236

The Problem 236

Scenario 1 - We have Access to the Order Object 236

Scenario 2 - We have Access to an Order Id 237

How to View All the Items in an Order 240

The Problem 240

The Solution 240

Accessing Shipping Items in the Order 241

How to Make Address Field Editable on the Order Admin Screen 244

The Problem 244

12
The Solution 245

13
How to Use This Book
Hello, and welcome to “Learning WooCommerce Development by Example”
by way of a short introduction, I just wanted to briefly go over how to use the
code in this book.

Many of the chapters in this book contain code that is added via hooks and
filters, before using any of this code in anger we would recommend you read
the “​do_action and add_action in WooCommerce and WordPress​” and
“​How to use add_filter and apply_filters in WooCommerce​” chapters to
get an understanding of how actions and filters work in
Wordpress/WooCommerce, as an understanding of these concepts will
underpin everything we cover in this book.

In order to understand how to add code snippets to WooCommerce you


should take a look at the “​How to add a Code Snippet to WooCommerce​”
chapter which goes through all the ways you can add code to WooCommerce.
We would also recommend that you take a look at the “​How to Create a
Storefront Child Theme​” chapter to understand how child themes work.

Some chapters contain code that is not added via actions and filters, these
chapters typically deal with code you’ll use in many different scenarios, for
example finding out what products are in a users basket. If you wish to try out
any of the concepts in these chapters we suggest you take a look at the “​How
to Add a PHP Page to WooCommerce​” chapter which shows you how to
create a WordPress page that you can use to experiment with.

Once you have an understanding of how to use the code snippets then the
book has been written in a way that allows you to skip from chapter to chapter.
So please just go where you want to, we hope you enjoy the book :)

14
The Basics
In this section we’ll take a look at how actions and filters work, we’ll also look
at how to add code snippets and explore creating our first child theme.

We’ll then take a look at some key concepts that you’ll hopefully be able to
use regularly in your WooCommerce projects.

15
How to add a Code Snippet to
WooCommerce
If you’ve thought about modifying your WooCommerce store then you’ve
probably come across code snippets online, code snippets can be useful and
provide the ability to add extra functionality to your store with a simple copy
and paste, but how should you add a snippet to your store?

What’s the best way to add a


Code Snippet to WooCommerce?
In this chapter, we’ll look at four different ways to add code snippets to your
store. Here are the four methods we’ll look at

● using the functions.php file in your theme


● using the functions.php file in your child theme
● using a code snippets plugin
● writing a plugin

For each method, we’ll detail how it’s done and then look at its pros and cons.

Using the functions.php File in Your


Theme

16
This may well be the most tempting way to add a snippet, it’s potentially very
quick, but it’s also probably the most unsafe method both in terms of keeping
the functionally you add and also in terms of breaking your site.

How it’s Done

There are two ways to make a change via this method

FTP

This is the safest way to make a change using this method, here’s how to do
it.

Use an FTP client such as FileZilla to connect to your website, to do this you’ll
need an FTP URL, username, and password, these should all be available
from your hosting company.

Once you have accessed your site via FTP use your FTP client to access the
“wp-content\themes\” folder within your website. From here choose the folder
that corresponds to the theme name you are currently running on your site.

In the screenshot above you can see how I’ve accessed the “storefront” theme
folder and found the functions.php file. To add a snippet to your site you
should just need to append it to the end of the “functions.php” file. Once you
have located the file then you can either edit it via the FTP client or download

17
it to your machine, edit it (we’d strongly recommend backing the file up first)
and then upload the changed file back to your server.

Via the WordPress Admin Dashboard

Before explaining this method, we really should point out that we strongly
advise against using it. It is quick, but it can also lead to your site being taken
offline.

We considered not including this method in the chapter at all because it’s so
dangerous but thought it was better to explain it rather than just ignoring it.

To make the change via the dashboard go to “Appearance -> Editor”, this
should open the editor, and you should see a theme files menu on the
right-hand side of the screen. From here choose the “functions.php” file

The menu may look different to the above depending on what theme you are
using.

The “functions.php” file should now open in the editor and you can add your
snippet before saving the file.

The real danger of this method is that if you copy in some bad code or make
an error when adding your code, then it could lead to your site becoming

18
unavailable, the most common way this manifests itself is when you visit your
site all you can see is a blank white screen.

Unfortunately, the admin area of your site also becomes unavailable, so


unless you have FTP access to your site then it will be incredibly difficult to get
your site up and running again.

The main advantage of the FTP Method over this one is that if you add some
bad code using the FTP method you can just edit the file again and remove
the offending code, using this method if you make any mistakes then you may
no longer be able to access the file editor.

Will the Editor Allow Me to Enter Bad Code?

In later versions of WordPress the editor checks code before saving, so this
should catch a number of cases where bad code is entered, but it would be
wrong to rely on this check.

The editor is able to check if code entered can be compiled, but it is possible
to add compilable code that would introduce problems that could bring down
your site, such as infinite loops.

So while there are some safeguards in place we would always advise the
utmost caution when using this method.

Pros and Cons of Adding Snippets to the functions.php File in


Your Theme

Pro

The only real pro to this method is that it’s quick.

Cons

19
As mentioned previously you could make your entire site unavailable using
this method, whilst making the change via FTP lessens the risk of this it still
makes it a risky option.

The other major drawback of this method is that when themes get updated the
“functions.php” file can be overwritten, this could lead to you losing all the
functionality that you have added via the “functions.php” file when you update
your theme.

Using the functions.php File in Your Child Theme

This method is a very similar method to the one above, the only difference is
that you’ll make changes to the “functions.php” file in a child theme rather than
directly to a theme.

Just like the theme “functions.php” method, you can do this via FTP or via the
admin interface (the admin interface will give you access to the “functions.php”
file in your child theme).

This method also presents the same dangers as the previous method, and if
you add bad code to your child theme “functions.php” you could still make
your store inaccessible.

Pros and Cons of Adding Snippets to the functions.php File in


Your Child Theme

Pros

This is the real difference to using this method, if you add the snippet to your
child theme then you won’t lose any added functionality when the child
theme’s parent updates. In theory, the “functions.php” file should never be
overwritten by updates to your site/themes.

20
Cons

These are pretty much identical to the cons of the previous method.

One other thing to point out is that adding snippets to your themes via
the”functions.php” file will mean you will lose all of the functionality you’ve
added if you ever change your theme. The next couple of methods that we are
going to look at will get around this problem.

Via a Third Party Plugin


You can avoid updating a “functions.php” completely by adding code snippets
via a plug-in, we’ll take a look at how to do this via the ​Code Snippets​ plugin.

Once you have installed the plugin then a number of options are added to the
WordPress admin area

If you choose “Add New” then you will be presented with an editor to add a
new snippet

21
As you can see from the screenshot above you are prompted if you are
entering bad code, the code editor also has an autocomplete function (which
can be turned off in the plug-in settings) so it will try to complete any brackets
or braces that you add to your code.

If you still go ahead and try and save some code that the plug-in considers
dangerous then it will refuse to save the snippet and show a message similar
to the one below

The plugin also allows snippets to be specifically targeted at the front or back
end of your site, so even if you add a snippet that wrecks your
customer-facing pages you should still be able to fix it in the back end.

If all else fails and you’ve added a snippet that both breaks your site and
you’re unable to get access to your admin area to fix it, then the plugin
provides a method where you can disable all added snippets by adding a line
of text to a WordPress settings file. You will need FTP access to your site to
do this though.

22
Another advantage of using the plugin is that snippets can be turned on and
off, so it’s easy to remove snippets without having to delete them
from/comment them out in the “functions.php” file.

Snippets can also be categorized using tags, so if you’re running a lot of


snippets on your store you can easily sort them into areas of functionality or
pages they run on, and then turn them on and off accordingly. It is also
possible to add titles and descriptions for each snippet. Being able to
categorize and describe snippets in this way makes it much easier to keep
track of your snippets, and is certainly preferable to scrolling through a text file
trying to work out what each added snippet does.

The plugin also has the functionality to add CSS and Javascript snippets to
your store which is something that would be much more difficult to do via the
“functions.php” file unless you had some coding knowledge. The plugin
provides examples of how to add Javascript and CSS so you should be able
to copy these and add the required styles or scripts to your store.

Pros and Cons of Adding Snippets via a Third Party Plugin

Pros

You don’t need to change any code files.

It’s much easier to categorize and organize your snippets.

You can add CSS and Javascript snippets reasonably easily.

Any snippets you add are not linked to your theme, so even if you swap
themes any snippets added via the plugin will still remain intact.

Cons

23
Although the plugin makes it hard to add bad code, it is still possible to break
your site by adding a snippet containing bad code. The plugin provides a
workaround for this but you’ll need FTP access to your site in order to
implement it.

If you’re doing client work then some clients may object to code being added
via a plugin, we will take a look at how to create our own plugins in the next
method.

Via a Plugin we Write Ourselves

WordPress makes it reasonably easy to create a plugin and we can use this
functionality to add snippets to our site, let’s take a look at how we would do
this.

Firstly, we will create a folder for a plugin in the “public\wp-content\plugins\”


directory of our WordPress site, the image below shows me creating a
directory using FileZilla

24
Once we have created the directory then we will create a php file with the
same name (hwn-snippets-plugin.php), we will then add the following code to
the file.

<?php

/**
* Plugin Name: Hard Working Nerd Snippets Plugin
* Description: An example to show how to add snippets via a plugin
* Author: Ian Preston
* Version: 1.0
*/

/* Put your snippets below here. */

/* Put your snippets above here. */

?>

As you can see from the code above we have not yet added any functional
code, but we have provided the information that WordPress requires to
register a plugin. We can confirm this by looking at the plugins page in the
WordPress dashboard, where we should see our new plugin has been added.

Now we can see our plugin, let’s add some code

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="'​ .

25
$product->get_permalink() . ​'">'​ . $button_text . ​'</a>'​;
​return​ $button;
}

The code above removes the “Add to Cart” button on the shop page and
replaces it with a “View Product” button that takes the user to the product
detail page. If we add the code above to the plugin file between the comments
telling us where to insert our code and then activate the plugin then we should
see the following on our shop page.

Now if we want to turn off our functionality then we can deactivate our plugin
and the original functionality is restored

26
One of the drawbacks to adding snippet functionality via a plugin is that a user
could inadvertently de-activate our plugin and then all of the associated
functionality would disappear from the site, we can get around this by
converting our plugin to a “must use” plugin.

Here’s how.

Firstly we’ll create a new folder in “\wp-content\” named “mu-plugins”.

Now if we move the “hwn-snippets-plugin.php” file from


“\wp-content\plugins\hwn-snippets-plugin\” to “\wp-content\mu-plugins” and

27
refresh the plugins section in the WP dashboard, we should see a new
“Must-Use” menu item.

Our plugin should now be displayed under this section

Now the plugin has been converted to a must-use plugin any functionality
included in the plugin will be turned on by default, and the plugin will only be
deactivated if it is removed from the “mu-plugins” directory.

Pros and Cons of Adding Snippets via a Plugin We Write


Ourselves

Pros

Any added functionality is not linked to the store’s theme, so any snippets
added will carry on working if the theme is switched.

Potentially looks more professional than using a third party plugin if you are
working for a client.

Snippets can be split into different plugins and then activated/deactivated as


required.

Cons

28
Unless a must-use plugin is used it is possible to disable functionality by
de-activating the plugin.

Not as easy to categorize and sort snippets as when using a third-party plugin.

Final Thoughts
There are a number of different ways to add snippets to your WooCommerce
store, each has its pros and cons and it is a matter of personal choice which
one you decide to use.

Having said that, there are a number of risks in adding snippets to your
“functions.php” file so unless you have good reasons for doing this we would
recommend you go with one of the plugin options.

29
do_action and add_action in
WooCommerce and WordPress
It’s quite hard to give an easy to understand definition of what ​do_action​ and
add_action​ do, so I’m just going to launch straight into some code and
hopefully the purpose of the two methods will become clear reasonably
quickly. Here’s a very simple example of do_action

do_action(​"display_some_numbers"​);

If we add the code above to a page then absolutely nothing will happen, so to
move things along a bit let's define a function that outputs a number.

function​ O​ utputOne​() {
​echo​ "​ one<br>"​;
}

Now we have a function, we can link it to our do_action call using the code
below

function​ O​ utputOne​() {
​echo​ "​ one<br>"​;
}

add_action(​"display_some_numbers"​,OutputOne);

Now the magic is starting to happen, the script above should now output
“one”.

So what has gone on here? When we call ​do_action​ we pass a tag to it, in our
case we pass the tag “display_some_numbers”, the code then checks if any
functions are linked to the tag and, if it finds any linked functions it executes

30
them. We link functions to the tag using the ​ add_action ​method, let’s take a
look at that line again.

add_action(​"display_some_numbers"​,OutputOne);

This line links the ​OutputOne ​ function we defined earlier to the


“display_some_numbers” tag, then when we call
do_action(“display_some_numbers”), ​the ​OutputOne ​function is called and it
outputs the text “one” to the screen.

Just to further emphasize what’s happening, we can add another couple of


functions similar to ​OutputOne​ and then change the code to call the
add_action ​function three times linking both functions to the
“display_some_numbers” tag

function​ O​ utputOne​() {
​echo​ "​ one<br>"​;
}

function​ O​ utputTwo​() {
​echo​ "​ two<br>"​;
}

function​ O​ utputThree​() {
​echo​ "​ three<br>"​;
}

add_action(​"display_some_numbers"​,OutputOne);
add_action(​"display_some_numbers"​,OutputTwo);
add_action(​"display_some_numbers"​,OutputThree);

do_action(​"display_some_numbers"​);

then we will get the following result

31
Changing the order the functions are
called in
If we wanted to display the numbers in reverse order (three, two, one), then
we could just change the order in which the ​add_action​ calls appear in the
code, but we can also pass a third argument to ​add_action ​which specifies the
order in which the functions will be called. The functions linked to the lowest
numbers will be called first, so if we change our code as below

function​ O​ utputOne​() {
​echo​ "​ one<br>"​;
}

function​ O​ utputTwo​() {
​echo​ "​ two<br>"​;
}

function​ O​ utputThree​() {
​echo​ "​ three<br>"​;
}

add_action(​"display_some_numbers"​,OutputOne,3);
add_action(​"display_some_numbers"​,OutputTwo,2);
add_action(​"display_some_numbers"​,OutputThree,1);

do_action(​"display_some_numbers"​);

Then we will see the output below.

32
Passing arguments to functions
Let’s change our ​OutputOne ​function so it accepts a couple of arguments that
can make the displayed text bold and/or italic

function​ ​OutputOne​($isbold,$isitalic) {
$text = ​"one"​;

​if​ ($isbold) {
$text = ​"<strong>"​.$text.​"</strong>"​;
}

​if​ ($isitalic) {
$text = ​"<em>"​.$text.​"</em>"​;
}

​echo​ $text.​"<br>"​;
}

How can we pass arguments to the functions we pass into the ​add_action
calls? We can pass the arguments into the ​do_action ​call as below

do_action(​"display_some_numbers"​,​true​,​true​);

Note that even though only the ​OutputOne ​function takes any arguments,
passing the arguments in to ​do_action ​has no adverse effects on the other two
functions, they just ignore the arguments. Having passed the arguments in we
might expect the text “one” to be shown in bold, italic font, but if we run the
code the text only appears in bold

33
Why has this happened? Well, by default, any functions passed as an
argument to the ​add_action ​function only get one parameter value passed to
them. If we want to pass more arguments then we need to explicitly state that
in the ​add_action ​call, as below

//explicitly state that the OutputOne function should have two


arguments passed to it
add_action(​"display_some_numbers"​,OutputOne,3,2);
add_action(​"display_some_numbers"​,OutputTwo,2);
add_action(​"display_some_numbers"​,OutputThree,1);

do_action(​"display_some_numbers"​,​true​,​true​);

Then the numbers are displayed as expected

Removing functions with remove_action


Let’s say we no longer want to display the number “two” in the list, here’s how
we could do that

remove_action(​"display_some_numbers"​,OutputTwo,2)

It’s worth noting that if you have supplied a priority/order argument when
adding the function with the ​add_action ​then you must supply the same
argument when removing the function.

34
Using the example above, if we did not supply the order argument then the
function would not be removed and the value ”two” would still be output.

Just as a final point on this, the ​remove_action ​function returns a boolean


value so you can use that to check if your function has been successfully
removed or not.

Why bother with do_action and


add_action couldn’t we just write stuff
directly to the page?
This is a good question, and if all we wanted to do was write three numbers to
the page then using do​_action​ and ​add_action​ would definitely be a case of
over-engineering.

do_action​ and ​add_action ​are however incredibly useful when we want to add
and remove functionality from code without getting involved in the nitty gritty of
the code itself. For example, imagine we have the following in a WordPress
theme file.

add_action(​"display_header"​,DisplayTitle,1);
add_action(​"display_header"​,DisplayTagLine,2);
add_action(​"display_header"​,DisplayMenus,3);

do_action(​"display_header"​);

You can see how simple it would be to manipulate the contents of the header
and how we could easily add/remove extra functionality without having to get
involved with template files etc. Indeed, we could just make our changes via
one of the methods described in the “​How to add a Code Snippet to
WooCommerce​” chapter and provided the ​do_action ​call remains in place our
changes will still work no matter how many updates the theme goes through.

35
do_action and add_action in
woocommerce
do_action​ and ​add_action ​are used extensively throughout the woocommerce
codebase and if you’re looking to make woocommerce modifications then
you’ll need a good understanding of how the two functions work. Hopefully,
this chapter has aided your understanding.

36
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 the “​do_action and add_action in


WooCommerce and WordPress​” chapter we’ll take a look at filters in this
chapter, we’ll firstly look at a contrived example to increase our understanding
and then 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

37
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. It then returns the new
value to the calling code.
● 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.

38
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

​ my_favorite_films'​, '
add_filter( ' ​ 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 occur 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, Dirty Dancing” (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”

Priority

39
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

40
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

41
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);

42
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
);

43
The code above can be found in
https://github.com/woocommerce/woocommerce/blob/master/templates/loop/a
dd-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 being 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

44
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;
}
add_filter( ​'woocommerce_loop_add_to_cart_link'​,
'hwn_replace_add_to_cart_button'​, 10, 2 );

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, there’s an example of this in the
code 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
function​ ​hwn_remove_add_to_cart_button​( $button, $product ) {
​return​ ​""​;
}

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

We will definitely see many examples of filters in the chapters ahead!

46
How to Create a Storefront
Child Theme
What are the Benefits of Creating a
Storefront Child Theme
Let’s imagine that you want to make a change to your WooCommerce store, if
the change can’t be made via options available in the
WordPress/WooCommerce admin, then you’ll need to write/alter some code
or CSS files.

If you’ve done any coding in the past your first instinct will probably be to find
the file that you need to change and start adding/editing code, but this is not a
good idea in WordPress because

● It’s reasonably rare to change one of the WordPress core files, as


WordPress uses a system of filters and hooks, so often when code changes
are made it will be via a hook or filter. If you’re not familiar with these concepts
then we recommend you take a look at the “do_action and add_action in
WooCommerce and WordPress” and “How to use add_filter and apply_filters
in WooCommerce” chapters
● WordPress and WooCommerce update regularly, and it’s possible that
if you change a file directly it will be overwritten in a future update, you could
then find that any changes you’ve made disappear from your store in the blink
of an eye (or the installing of an update)

Child themes solve the problems above by

47
● Providing a safe location where you make changes to files safe in the
knowledge that they won’t be touched by any updates
● Providing a mechanism whereby WordPress will check your child theme
for a file before it loads it from the main theme.

It’s going to be easier to understand some of the concepts above with worked
examples, so let’s go-ahead and create a Storefront child theme.

How to Create a StoreFront Child Theme

To demo this I’m going to use a site running on Local by Flywheel on a


Windows machine.

The first thing to do is to navigate to the “\wp-content\themes” of your website,


on my machine, it looks like this

We’ll then create a directory for the child theme, for the purpose of this demo
I’m going to call it “hardworkingnerdstorefront”

48
We then need to create a file in the directory called “style.css”

We then need to add some content to this file that will help WordPress identify
our child theme, here’s what I added for this example

/*
Theme Name: Hard Working Nerd Storefront Child Theme
Version: 1.0
Description: Hard Working Nerd Storefront Child Theme Example.
Author: Ian
Author URI: https://www.hardworkingnerd.com
Template: storefront */

The “Theme Name”, “Version”, “Description”, “Author” and “Author URI” fields
should all be fairly self-explanatory, the “Template” field is the really important
one here as that tells WordPress what to use as the base theme for our child
theme.

If we look back at the image of the “\wp-content\themes” directory we can


see it lists the following theme directories

● storefront
● twentynineteen
● twentytwenty
● twentyseventeen
● twentysixteen

We could use any of these directory names as the value for “Template” and
that would cause WordPress to use that theme as the basis for our child

49
theme. In this example, we want to use the “storefront” theme so we provide
the value “storefront” for “Template” in our “style.css”.

Activating the Child Theme

To activate your child theme, from the WordPress admin area choose
“Appearance -> Themes”, your theme should then be visible

As you can see, because I went with quite a wordy title we can only see a bit
of the title of the theme but we can tell that it’s definitely our theme, if we now
click the “Activate” theme WordPress will start using our theme.

Once our theme is activated, if we take a look at the “shop” page in our store
then it should look exactly as it did when we had the “Storefront” theme
selected

50
this is because we have not yet modified anything in our theme that will
override anything in the “Storefont” theme, we’ll start making some
modifications to our theme in the next few sections.

How to Upload Your Theme to a Remote Site

In the example we have been looking at so far we have done everything


locally, but what if we wanted to create a child theme on a remote site (i.e. a
website that was hosted on a server). In this scenario, we would have two
options –

FTP

If you have access to your site via FTP you could follow the steps above using
your favorite FTP client. An FTP client should allow you to create a directory,
name it and create a “style.css” file with your desired values, you should then
be able to activate the theme as in the example above.

51
Upload your Child Theme as a Zip File

You can also upload your child theme as a zip file, to do this create your child
theme locally as we have done in the example above, once you’ve created a
directory and added a style sheet file, then create a zip file from your directory.

The easiest way to do this on Windows is to right click your directory

Then choose “Send to” -> “Compressed (zipped) folder”, this should create a
zip file with the same name as your folder

Now if you go to “Appearance -> Themes” in the WordPress admin


dashboard

and click the “Add New” button

Then via the “Upload Theme” button you’ll be prompted to upload a zip file

52
If you choose the file that we just created then it should be possible to activate
your new child theme as we did before.

What Changes Can I Make with my Child


Theme?
Style Changes

As we already have a “css” file in place adding style rules to our new child
theme is an easy task, let’s say that we want to change the header on the
shop page, so it’s bigger, greener and in capitals.

Then we can add the CSS code below to the “style.css” file

53
h1.woocommerce-products-header__title.page-title {
color: green;
text-transform: uppercase;
font-size: 50px;
}

The shop title will then be permanently changed, as long as our child theme is
in place and activated.

Changes via the functions.php File

As mentioned earlier, WordPress and WooCommerce implement a system


based on filters and actions that allow you to change the behavior of the code
without having to directly alter the files that contain the PHP code.

As an example, let’s consider that we want to remove the “Add to Cart” button
on the shop page and replace it with a “View Product” button, the new button
should send the user to the product’s details rather than adding an item to the
cart.

We can do this by firstly creating a file called “functions.php” in our child


theme folder, then adding the following code to the file

<?php
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;
}

Once this code has been added when we navigate to the “Shop” page we
should see the new button in place

54
Changes made by Altering Files

It is also possible to make changes by altering code in files, as mentioned


earlier it is not a good idea to directly change the core
WordPress/WooCommerce files as if the files are changed in a later update
any changes we have made will be wiped out.

Child themes give us an opportunity to change files without altering the core
files, let’s take a look at how this works.

On the product details page in WooCommerce an SKU code is displayed

55
We will use our child theme to remove the SKU by changing a file.

The first thing to do is to find the file that contains the code that adds the SKU
code to our page, the code is contained in this file –

https://github.com/woocommerce/woocommerce/blob/master/templates/single
-product/meta.php

At the time of writing the code that displays the SKU Code starts on line 28
and ends on line 32 of the file

So that is the code that we need to remove, but we first need to add the file to
our child theme. In my local WordPress site, the file exists here –

\wp-content\plugins\woocommerce\templates\single-product

56
to make a copy in our child theme folder we need to copy the file structure
from the “woocommerce” folder and remove the “templates” folder here’s a
screengrab of the file in the correct folder in my child theme

Just make that a bit clearer, we copied the file from

\wp-content\plugins\woocommerce\templates\single-product\meta.php

to

\wp-content\themes\hardworkingnerdstorefront\woocommerce\single-product\
meta.php

Following the convention above it is possible to replicate any of the files


contained in the “woocommerce” folder in our child theme.

If you open the “meta.php” file in a text editor it actually contains instructions
of how to copy it to a child theme

If we now delete lines 28-32 in our copy of the file and visit the product details
page again then the SKU code should have been removed.

57
As well as altering WooCommerce files it is also possible to change files in the
Storefront theme itself, let’s take a look at the “header.php” file

If we copy the file from the “storefront” theme directory –

\wp-content\themes\storefront

to our child theme directory

\wp-content\themes\hardworkingnerdstorefront

Then we can make changes to the file and they will appear in our store as
long as our child theme is activated, to demo this functionality we’ll just add
another div to the header

58
We should then see our changes when we view the store in a browser

A Note About Enqueuing Styles


If you read documents about creating a child theme you may see people
mentioning enqueuing parent style sheets, in some cases you need to do this
to ensure any styles contained in the parent theme are pulled into the child
theme.

I’m not going to go into this in any great detail as I think it will only create
confusion, and it’s not really in the scope of this chapter, the only thing to take
away about this is if you are creating a child theme based on Storefront you
do not need to worry about adding parent style sheets as code has already
been added to the main Storefront theme that takes care of all this for you.

If you want more information on this I’d recommend reading the section in the
WordPress Theme Developer Handbook about enqueuing styles, you can find
that ​here​.

59
Is There a Way to Add Permanent CSS
and Code Changes Without Creating a
Child Theme
There is no way to edit files in WooCommerce or a parent theme without
creating a child theme, but there are a couple of plugins that will allow you to
make modifications without creating a child theme.

Code Snippets​ – Allows you to add code snippets via the WordPress admin
area

Simple Css​ – Allows CSS to be added via the admin area of your site, also
allows CSS to be targeted at specific pages

How to Add Javascript to Your Child


Theme
In this final section, we’ll take a look at how to add some Javascript to our
child theme, to do this we’ll need to –

● Create a Javascript file and add it to our theme


● Add some code that loads the Javascript file into our site so the code
gets executed in the browser.

We’ll begin by creating a “scripts” folder in our child theme and adding a file
called “examplejavascript.js”

60
We’ll then add some really simple Javascript (that you probably wouldn’t want
to use on a production site)

$(document).ready(​function​() {
alert(​"Hello there!"​ );
});

Now we have some Javascript we just need to add it to our pages, to do that,
we’ll add the code below to the “functions.php” file in our child theme

function​ ​hwn_child_theme_add_javascript_scripts​() {
wp_enqueue_script( ​'example-js'​,
get_stylesheet_directory_uri() .
'/scripts/examplejavascript.js'​, ​array​( ​'jquery'​ ),​''​,​true​ );
}
add_action( ​'wp_enqueue_scripts'​,
'hwn_child_theme_add_javascript_scripts'​ );

Here’s what the code does

● Uses the wp_enqueue_script function to add our Javascript file


● The first argument passed to the functions identifies our javascript file,
the second argument tells WordPress where to find our file (notice that we use
the ​get_stylesheet_directory_uri() f​ unction to get the path to our child theme),
the third argument lets WordPress know that our script has a dependency on
jQuery, we pass false to the fourth argument so WordPress adds a version
number our script, and finally, we pass true so WordPress will load our script
in the site’s footer)

61
62
Understanding Attributes in
Woocommerce
Although Woocommerce uses categories like a normal WordPress blog, it
also adds the concept of attributes that can give you and your users finer
control when categorizing and searching for products. In this chapter, we’ll
take a look at how to set attributes up and what you might use them for. We’ll
also take a look at how you can access attributes in your PHP code.

Why would you use Attributes in


Woocommerce?
Why do we need attributes when we already have categories?

Well, let's imagine we have a clothing store that sells shirts, trousers, socks,
and shoes. We could use categories to separate our products which would
leave us with categories for shirts, trousers, socks, and shoes. But what if we
also wanted to categorize our products by color? This would allow customers
to search for, say orange items, and that would allow them to buy a matching
orange ensemble (man – they’re going to look good!).

Creating Attributes in Woocommerce


Before we start creating an attribute it’s worth pointing out that there are two
types of attributes in Woocommerce. Global attributes, that can be applied to
all products in your store and custom product attributes that only apply to a
single product.

Adding a Global Attribute

63
Adding an attribute is relatively simple, from the left-hand menu choose
“Products” and then “Attributes”, the fields you have to fill in are fairly straight
forward so I won’t go through them here

Once you’ve added an attribute, you can add “terms” to it, we can think of
terms as children of the attribute. So if we created an attribute named “Color”
we might expect it to have terms (or children) named red, green, yellow and
blue.

64
Adding a Custom Product Attribute

To add a custom product attribute go straight to the “Attributes” tab on the


products edit screen, from there choose the “Custom product attribute” from
the dropdown. You should then fill in the fields as prompted.

What’s the Point of Custom Product Attributes?

Given that you can’t filter using custom product attributes you might wonder
what the point is. Adding a custom attribute to a single product just links the
attribute and its terms to that product, and aside from displaying the attributes
on the product page, you’ll get no other benefits unless you are running any
custom code or plugins.

Custom attributes do perform a more important function when used in


conjunction with variable products though, we won’t go into this functionality in
this chapter but it’s definitely something worth looking into if you want to glean
some further understanding about attributes.

65
Filtering by Attributes in Woocommerce
The first step to filtering by an attribute is to assign an attribute to a product,
you can do this by editing a product and selecting the “Attributes” tab

Once you’ve accessed the tab you can choose which attribute you want to
add from the “Custom product attribute” dropdown, once you’ve chosen the
attribute you require Woocommerce allows you to choose specific terms, you
can see from the example below that I’ve added the “Blue” term to the
product.

66
If you check the “Visible on the product page” checkbox then, when you visit
the product’s page you will see the attributes you’ve added listed within the
“Additional information” area of the screen.

If you want the user to be able to drill-down into a category and see other
products that share the same attribute (which would be other blue products in
this example) then if you check the “Enable archives?” checkbox on the edit
attribute screen then Woocommerce will automatically do this for you.

Once this is done, clicking on “blue” will show you a page of all the blue
products in your store.

67
If you look at the URL of the page displaying the attribute products you should
see something similar to – “http://yourwoosite.com/color/blue/”, the
SEO/prettified link is also due to the “Enable archives?” checkbox on the edit
attribute screen being checked. If the option had not been checked then the
URL wouldn’t work, but you could still access the terms using a non-prettified
URL. In the example above the following URL would show you all the “blue”
products “http://yourwoosite.com/?taxonomy=pa_color&term=blue”.

The “Filter Products by Attribute” widget

If you want to provide users with an easy way to filter products by attribute,
Woocommerce provides a widget that you can add to your site. The “Filter
Products by Attribute” widget has two different display modes

You can show a list of attributes

68
Or a drop-down list

69
You can also choose whether to do an “and” or “or” search on the attributes.
There are more sophisticated filtering options available as paid plugins, but as
a free option, the “Filter Products by Attribute” widget is definitely worth
considering.

Accessing Product Attributes via PHP

The snippet below will output a single (it won’t work for variants) product’s
attributes and terms beneath the product’s description.

function​ ​hwn_get_attributes_and_terms_for_product​() {
​global​ $product;
$attributes_and_values = ​array​();
​foreach​ ($product->get_attributes() ​as​ $attribute) {
​if​ ($attribute->is_taxonomy()) {
array_push($attributes_and_values,
$attribute->get_taxonomy_object()->attribute_label.​":
"​.$product->get_attribute( $attribute->get_name() ));
}
​else​ {
array_push($attributes_and_values,
$attribute->get_name().​": "​.implode(​", "​,
$attribute->get_options() ));
}
}

​echo​ implode(​"<br>"​,$attributes_and_values);
}

add_action( ​'woocommerce_single_product_summary'​,
'hwn_get_attributes_and_terms_for_product'​, 25 );

If you want to list all of the product attributes in a store and provide links to a
filter for each of the terms below each attribute then the code below should do
the trick.

70
global​ $wc_product_attributes;

$attributes = $wc_product_attributes;

if​ ( $attributes && ! ​empty​( $attributes ) ) {


​foreach​ ( $attributes ​as​ $attribute ) {
$terms = get_terms(​array​( ​'taxonomy'​ =>
'pa_'​.$attribute->attribute_name));
​foreach​ ($terms ​as​ $term) {
$term_id = $term->term_id;
$term_link = get_term_link( $term,
'pa_'​.$attribute->attribute_name );
$term_name = $term->name;
​echo​ ​'<a class="ccats" href="'​ . $term_link .
'"><span class="label">('​ . $attribute->attribute_label. ​") - "
. $term_name . ​'</span></a><br>'​;
}
}
}

71
Understanding Woocommerce
Variable Products
Variable products are a really useful tool in your Woocommerce toolbelt, but
there’s a bit of a learning curve in regards to setting them up. In this chapter,
we’ll take a look at how and why you would set up a variable product.

What are Variable Products and when


should you use them?
Let’s imagine you want to sell a napkin on your Woocommerce store, you
could do this by creating a simple product and then go from there.

But what if you wanted to sell the napkin in five different colors, then you’d
have to create another product for each of the different colors you wanted to
sell. Let’s then imagine that you also wanted to include a napkin ring with
each napkin and they also came in five different colors, that would then mean
you’d have to create a new product for every napkin and napkin ring color
combination.

In a situation like this, variable products really come into their own, instead of
creating multiple products you can create one product and then allow the user
to choose the color combination they require. Once you’ve set everything up
then Woocommerce will provide an interface for the customer to choose the
product options they require.

Here’s an example of a variation product interface for the example above

72
How to set up a variable product
To view the two drop down boxes above on the product screen you’ll need to
perform some configuration steps first. In your Woocommerce admin area
create a new product, then set the Product data drop down to be “Variable
product”

Now, before we do anything else, we’ll take a moment to consider categories


and attributes on a Woocommerce site.

Categories and Attributes on a Woocommerce site

On a normal WordPress blog, we would divide our posts up into categories


that would allow people to drill down into specific areas of our site. For
example on the www.hardworkingnerd.com site, we have a number of
Woocommerce articles and a few articles about c#, if a reader wanted to see
all the Woocommerce articles then they could go to the Woocommerce
category page and have a good read of the articles there.

73
On a Woocommerce site, you also have categories, so you could, for
instance, have a “t-shirt” category and then if a customer came to your store
looking for t-shirts they could immediately look at all the t-shirts you have for
sale.

In addition to categories, Woocommerce also adds an additional way of


classifying products named “attributes”, you would normally use attributes to
classify products in a way that went across category boundaries. So, if you
were running a clothing store you might introduce a “color” attribute and that
would allow you to group all types of clothing under the same attribute. This
would then allow your customers to see all the items in your store that were
red, so if they came to buy a red t-shirt, they could also get themselves a pair
of red socks and sandals at the same time.

Categories and Attributes and Variable Products

The reason why we went on that brief tour of attributes and categories is that
they are instrumental in setting up variable products.

When we set up a variable product we need to decide what variations of the


product there will be. In the example we used at the start of this article we
talked about Napkins that you could buy in different colors with different color
napkin rings, so let’s have a look at how we could go about setting that up as
a variable product.

If we go to the “Attributes” tab of our newly created variable product then we


should see something similar to the screenshot below

74
In the store I’m using for this example I already have two attributes, one
named “Color” and one named “Size”, so we can see those two attributes in
the drop-down. If I select “Color” and then click “Add” I see the following –

Now we can see two checkboxes if only the “Visible on the product page”
checkbox is checked then the attributes will be listed on the product
information page and it will be possible to filter/search for the product using
the “Color” attribute. You can find more details about this in the
“Understanding Attributes in Woocommerce” chapter.

75
If the “Used for variations” checkbox is checked then WooCommerce will
make it possible to create variation products for all of the variable
permutations created by the attributes, we’ll look at how to do this shortly.

So, having set-up the variable products for the colors we now need to set up
the variable products for the napkin rings.

For the colors we used a global attribute as there was already one defined
that suited our use case. For the napkin ring attribute, there is little point
creating a global attribute as napkin ring color is an attribute that will only
apply to napkins.

WooCommerce allows us to create an attribute that is specific to a product by


choosing the “Custom product attribute” option

Once this option has been chosen you’ll be presented with the following fields

You can see how we’ve filled in the fields to reflect what we need from napkin
ring attribute, note how we’ve entered the value “Red | Blue | Yellow” for the
value field, this is because, for custom attributes, WooCommerce requires a
pipe-delimited list of the possible values.

If we now save this custom attribute, as we have checked the “Used for
variations checkbox” then WooCommerce will make it possible to create

76
variations based on the attribute’s values, we’ll look at how to do this in the
next section.

How to create the variable products


At this point, if we visit the product page we will see the following

This is because we have not yet created any variations, let’s do that now.

If we go back to the product’s admin screen and choose the “Variations” tab
on Product data box then we’ll see a drop-down that gives us two options
“Add Variation” and “Create variations from all attributes”

77
If we choose “Add Variation” and then click the downward facing arrow then
we’ll see the following screen

If we set a price for the variation and click save, because we have chosen the
“Any Color” and “Any Ring Color” options then WooCommerce will allow users
to buy any of the napkin/napkin ring combinations (a yellow napkin with a red
napkin ring, a yellow napkin with a blue napkin ring, a yellow napkin with a

78
yellow napkin ring, a red napkin with a red napkin ring, etc.) and charge them
the price we have specified.

But what if we want to make the variations different? Let’s say we want to
charge more for a particular napkin/napkin ring combination.

In order to do this, we should select “add variation” again then choose the
color and napkin ring color we want to charge more for in the drop downs. We
can then set the price as before, but because we are now working with a
specific variation then the price will only be applied to that variation.

We can see that the price change has been applied correctly by going to the
product page and choosing the attributes that we have set the different price
against, once we’ve done that, the correct price should be shown.

As well as the price we can also set other fields for each specific variation. For
instance, we might want to set a different image for each variation or provide a
specific description for each variation. If we want to do something like this
adding each variation one by one will be a time-consuming process. Luckily

79
WooCommerce provides us with an option to create a variation for each of the
attribute combinations for a particular product.

Choosing this option still gives the user the same number of variations to
choose from as when we only created a single variation by choosing the “Any
Color” and “Any Ring Color” options but when we choose the “create
variations from all attributes” option we are able to set the properties against
each of our variations rather than sharing properties between all of the
variations.

Once both the “Color” and “Ring Color” attributes have been saved against the
product then when we hit the product page for the napkins the user will be
able to specify the napkin variation that they want to buy and add them to their
basket.

80
Setting up a Woocommerce
Development Environment with
Local by Flywheel and Visual
Studio Code
In this article, we’re going to take a look at how to set-up a development
environment for Woocommerce, by the time we’ve finished we should have an
environment that will allow us to –

● Quickly spin up a fresh Woocommerce site that includes test data


● Edit code productively using an IDE
● Step through code using a debugger

To set-up our test dev environment we will use the following tools –

● Local by Flywheel (a tool that allows us to easily create development


websites)
● Visual Studio Code (a code IDE)

Local by Flywheel
Local by Flywheel allows us to spin up new development environments quickly
and easily. If you didn’t use a tool such as Local you would need to install a
web server and a database server on your local machine. You would then
need to configure the web server to run multiple sites, then install WordPress

81
and Woocommerce on each of these sites, and also go through the process of
setting up the database server.

Local removes a lot of these complications by letting you set up websites,


without needing to install a web or database server. It also allows you to
create websites based on templates, so you only need to perform set-up steps
once and then you can repeatedly use them via the templating system. So
let’s take a look at how we can set-up Flywheel.

Firstly download and install Flywheel from ​https://local.getflywheel.com/​. You


can download a free version of the software for either Windows or Mac.

After you’ve downloaded Local, run through the install process, it’s fairly
straightforward so I won’t go through it here. Once Local is installed we can
start setting up a website.

Local makes setting up a site really simple, firstly, input your site’s name

82
Local will then ask if you want to create a “Preferred” or “Custom” site,
choosing “Preferred” is slightly quicker but Local only provides debug support
for “Custom” sites, so we’ll choose that option.

Once you’ve chosen Custom it’s fine just to accept the defaults. You’ll then be
asked to provide a WordPress username and password for the site, so enter
these and then click “Add Site”.

Local should then go ahead and set up the site for you, once it’s done you’ll
see the site listed in the left-hand side of the screen.

83
You should now also be able to view the site in a browser.

Now we have a WordPress site, we’ll install Woocommerce, this will allow us
to take a blueprint of the site later and then we will be able to create future
sites using that Blueprint. This allows us to quickly get to a point where we
have a clean install of Woocommerce ready to develop and test on.

To install Woocommerce search for it from the Plugins page in the admin area
of WordPress, then install and activate it. You’ll need to fill in the store details
following the activation.

I typically use the Storefront theme so after installing Woocommerce I install


that, but you can go ahead and install a different theme if you prefer.

Finally, I install some sample product data as this allows for quick and easy
testing of code snippets and plugins. To do this, navigate to the “Products”
page in the admin area of Woocommerce. You should see an “Import
products from a CSV file” button.

84
Click the button and you’ll be prompted to choose a CSV file to upload

When you install the Woocommerce plugin it comes with some sample
product data, so we can use that to set our products up. If you’ve been
following up to now on a Windows machine and not changed any of the
defaults you should find the sample product data here “C:\Users\<your user
name>\Local
Sites\testsite\app\public\wp-content\plugins\woocommerce\sample-data”,
select the “sample_products.csv” file and then keep accepting the defaults on
the next few screens until you see the done message.

We should now have a Woocommerce store set-up with sample products.

85
So let’s move on to setting up the code IDE.

Visual Studio Code

You can download Visual Studio Code ​from here​.

Once Visual Studio Code has been installed we need to open the website we
just created. Choose “Open Folder” from the file menu and open the folder
containing the new website. If you’re on a Windows machine then the website
should be in a folder similar to this “C:\Users\<your user name>Ian\Local
Sites\testsite\app\public”.

Once you’ve opened the folder click the debug icon on the left of the screen to
open the debug tab

You should now see a screen similar to the one below

86
Click the “create a launch.json file” text.

You will be presented with drop down list

Choose the “More…” option, you should now be presented with a list of
debugger extensions.

Here we are looking for an extension named PHP debugger, you might see it
straight away, but if not you can enter a search term

87
Once you have found the extension, click the “Install” button

Now choose “Add Configuration…” again and then choose the “PHP” option.

You’ll now be shown a configuration script named “launch.json”, the contents


should look similar to the code below.

88
{
​// Use IntelliSense to learn about possible attributes.
​// Hover to view descriptions of existing attributes.
​// For more information, visit:
https://go.microsoft.com/fwlink/?linkid=830387
​"version"​: ​"0.2.0"​,
​"configurations"​: [
{
​"name"​: ​"Listen for XDebug"​,
​"type"​: ​"php"​,
​"request"​: ​"launch"​,
​"port"​: ​9000
},
{
​"name"​: ​"Launch currently open script"​,
​"type"​: ​"php"​,
​"request"​: ​"launch"​,
​"program"​: ​"${file}"​,
​"cwd"​: ​"${fileDirname}"​,
​"port"​: ​9000
}
]
}

Once these changes have been saved we need to make a change to the
“php.ini” file, if you’ve been following along on a Windows machine you should
find the file here “C:\Users\<your username>\Local
Sites\testsite\conf\php\php.ini.hbs”. In the file search for the text “[Xdebug]”,
that should bring you to a set of configuration settings, you need to add the
following lines

xdebug.remote_enable = 1
xdebug.remote_autostart = 1

89
Note that the php.ini file has a “.hbs” file extension, this is a handlebars
template file that Local by Flywheel uses to create a php.ini file when the site
is run. So you’ll need to stop and restart your Local by Flywheel site in order
for any changes to take effect.

The notes of the PHP Debug extension contain up to date information about
the settings you need to add to get Xdebug working so it’s always worth
checking the notes if you do have a problem.

We should now be all set-up for debugging, to help us test let’s add some
code to the “functions.php” file. You should be able to find the function file in
the following place in the directory tree in Visual Studio Code
“wp-content\themes\storefront” (you’ll obviously need to go to a different
directory if you did not install the Storefront theme :)).

Once you’ve found the file add the following code after the first “<?php” script
tag in the file

add_filter( ​'posts_request'​, ​'dump_request'​ );

function​ ​dump_request​( $input ) {


​return​ $input;

90
}

The script will fire every time the “posts_requests” filter fires, the script has no
functional use so you should remove it once you’re happy that debugging is
working.

Once you’ve added the code we need to add a breakpoint, you can do this by
clicking in the left-hand-side gutter of the text editor in Visual Studio Code.
After clicking, a red dot should appear as below.

We now need to get Visual Studio Code to listen for our breakpoints, we can
do this by clicking the green arrow next to the “Listen for XDebug” text in the
debug panel of Visual Studio Code.

On clicking the arrow you should see a toolbar appear similar to the one below

Now when you call the shop page of the site we created in a browser you
should see the breakpoint get hit (a yellow arrow will appear around the red

91
dot – as below). You are now free to inspect variables and step through your
code to your heart’s desire.

Turning The Site into a Blueprint


Now we have everything set up we can create a blueprint from our new site,
this will allow us to easily spin-up a Woocommerce site that is ready to debug.
As everything we have set up so far will be included in the sites blueprint we
shouldn’t have to repeat any of the steps again, we can just create a new site
from the blueprint and we’re ready to go.

To create a blueprint right-click the sites name in the left-hand “Local Sites”
panel of Local

92
On the next screen enter a name for the blueprint and then click the “Save
Blueprint” button. Local should now save the blueprint for future use.

Having saved our blueprint we can use it to create a new site, just click
“Advanced Options” on the add site screen.

93
Conclusion
So we have seen how we can use Local by Flywheel and Visual Studio Code
to easily create new websites and debug them. We can also drastically cut
down on the time it takes us to install plugins and themes on multiple sites.

94
How to Add a PHP Page to
WooCommerce
When I first started messing around with WordPress I wanted to add a PHP
page to a site that I could use as a sandbox to test code, I ideally wanted the
page to be able to access all of the WordPress libraries and functions so I
could test WordPress functionality and not just PHP code.

Similarly, when I first started using WooCommerce I wanted the same thing,
and as it turns out, it’s not difficult to do at all and it's a really useful trick to
know when you’re doing Woocommerce development, so in this chapter we’ll
look at how to set-up PHP pages.

Creating the PHP Page

To create the PHP Page just create a file with a .php extension in the root of
your theme folder, this should work for both themes and ​child themes​.

Then add some code similar to this in the file

<?php
/*
Template Name: HWN Template
*/

95
The important part of the code is the value that comes after “Template Name:
” this value can be anything you like, but we will see the value displayed in the
WordPress admin area shortly.

Creating a WordPress/WooCommerce
page that uses the PHP File
Now we’ll hook up our PHP file to WordPress, to do this go to “Pages” -> “Add
New” from the WordPress admin area. What we do next will depend if we’re
using Guttenberg or classic editor

Guttenberg

Open out the “Page Attributes” menu in the “Document” section at the
right-hand side of the screen

96
As you can see from the screenshot above this should reveal a Template
drop-down, you should now be able to set this drop down to the value we
typed after “Template Name: ” in our PHP page earlier –

Classic Editor

In the Classic Editor, the “Page Attributes” section should be immediately


available on the right-hand side of the page, where you’ll be able to see a
“Template” drop-down

As with the Guttenberg method, you should now be able to set this drop down
to the value we typed after “Template Name: ” in our PHP page earlier –

97
Once the “Template” type has been set if we either save a draft of our page
and view the preview or publish the page and view it directly then we should
see a blank page that is using our PHP page as a template.

Testing the New Page


We can prove everything is working by adding some PHP code to our
template

<?php
/*
Template Name: HWN Template
*/

echo​ ​"Hello There<br>"​;

Now when we visit our page we should see the code we added

As mentioned earlier, providing the page has been created in a WordPress


site with the WooCommerce plugin installed then we can also use all of the
WooCommerce functionality, as an example, let’s list the buyer name of order
number 65 (you’ll obviously need an order with an id number of 65 in your
store for this code to work)

98
<?php
/*
Template Name: HWN Template
*/

echo​ ​"Hello There<br>"​;

$order = wc_get_order(65);
$order_data = $order->get_data();
echo​ $order_data[​'billing'​][​'first_name'​] . ​" "​ .
$order_data[​'billing'​][​'last_name'​];

The code above would show the following output (you’re likely to see a
different buyer name :))

It’s worth noting that the page we’ve created won’t be able to access any of
the content added to the page, as we don’t have any code to access ​the
WordPress loop​. If you did want to access this functionality you could take a
copy of the “page.php” file from your theme, add the “Template Name: ” code
to the top of the file and then start modifying/testing things from there.

Final Thoughts
Hopefully, this little trick will be helpful to you. Finally, credit where credit is
due, this chapter was completely inspired by ​this Stack Overflow
question/answer​.

99
How to Detect Category and
Tag Pages in Woocommerce
The Problem
When adding functionality to a Woocommerce store we sometimes want to
add features that should only be present on category or tag pages. In this
article, we’ll take a look at how we can detect category and tag pages in our
Woocommerce code.

The Solution
This code will detect if the user is on a category page

//returns true if the current page is a category page


is_product_category();

//returns true if the current page is the category page specified


in the passed argument
is_product_category(​'shirts'​);

//returns true if the current page is any of the category pages


specified
//in the passed array
is_product_category(​array​(​'shirts'​, ​'games'​ ));

//also works with numerical ids


is_product_category(18);
is_product_category(18,19,20);

100
The code for detecting a tag page is very similar

//returns true if the current page is a category page


is_product_tag();

//returns true if the current page is the tag page specified in


the passed argument
is_product_tag(​'puzzles'​);

//returns true if the current page is any of the tag pages


specified
//in the passed array
is_product_tag(​array​(​'jigsaws'​, ​'toys'​ ));

//also works with numerical ids


is_product_tag(18);
is_product_tag(18,19,20);

101
How to Generate Links To
Categories and Tag Archives
Problem
Online stores will typically need to link to the categories of products sold in the
store, but they may not want to hard code the links into the sites HTML as
changes to the URL structure could then break links within the site.

One way around this problem is to use PHP code to generate the links for us.
In this chapter, we’ll take a look at how to programmatically generate links to
WooCommerce categories and tags.

Solution
The solution is pretty simple, we need to utilize the WordPress ​get_term_link
function

//generates a link to the hoodies category


get_term_link(​'hoodies'​,​'product_cat'​)

//a numerical id for the category can also be used


get_term_link(44,​'product_cat'​)

Here we use the ​get_term_link f​ unction, we pass it two arguments

● The first argument is an id for the category, this can either the
categories numerical id or it’s slug
● The second argument is the term or taxonomy name, in this
case, we pass “product_cat” which is the name given to
WooCommerce categories

102
As product tags and categories are both terms/taxonomies we can generate
links for tags using ​very​ similar code

//generates a link to the hoodies category


get_term_link(​'fruity'​,​'product_tag'​)

//a numerical id for the category can also be used


get_term_link(44,​'product_tag'​)

As you can see, the code is almost identical, we just pass a different term or
taxonomy name.

Generating Links to Multiple Categories


or Tags
Another benefit of this approach is that we can use it to quickly generate
multiple links, in the code below we use the ​get_term_link f​ unction in a
foreach loop to generate a link for every category in our store

$product_categories = get_terms(​'product_cat'​, ​array​());

foreach​ ($product_categories ​as​ $category) {


echo​ ​"<a href='"​ . get_term_link(
$category->term_id,​'product_cat'​ ) . ​"' target='_blank'>Take me to
the "​ . $category->name . ​" category<br>"​;
}

Here we use the ​get_terms ​function to return a collection of categories, we


pass two arguments to ​get_terms

● The term or taxonomy name, in this case, we pass “product_cat”


as we only want to see product categories
● An arguments array, as we are happy with the defaults we just
pass a blank array

103
​ nd the
We then loop over the collection of categories using ​get_term_link a
category name to generate a list of links, the code above will produce the
output below

As with our previous link generating example, we can achieve the same result
for tags with similar code

$product_tags = get_terms( ​'product_tag'​, ​array​() );

foreach​ ($product_tags ​as​ $tag) {


echo​ ​"<a href='"​ . get_term_link( $tag->term_id,​'product_tag'
) . ​"' target='_blank'>Take me to the "​ . $tag->name . ​" tag<br>"​;
}

Aside from some differences to the output, the only real difference here is that
we pass “product_tag” as the term/taxonomy name.

Excluding Empty Categories/Tags

You may have noticed that in the categories example above, we returned the
“Uncategorized” category. Obviously, I run a very well organized store, so I
don’t have any products in that category. So how could I stop this category
from being displayed?

104
Luckily the arguments array that we pass to the ​get_terms ​function can handle
this for us. We just need to add a value/key pair to the array that specifies that
we don’t want empty categories to be returned.

$product_categories = get_terms( ​'product_cat'​, ​array​(​'hide_empty'


=> ​true​) );

Conclusion

The WordPress ​get_term_link ​function allows us to easily generate links to


category and tag pages, we can also use it in conjunction with the ​get_terms
function to generate lists of links to categories and tags in our store.

105
How to Add a Custom Field to
a Product
The Problem
Adding a custom field to a product in WooCommerce is a very popular
request, and whilst it might seem a little daunting to add a field and then get
the data entered into the field to display throughout the cart, order, and
back-end order process we can actually accomplish the task using only a ​few
actions​ and filters.

Let’s take a look at what we need to do.

The Solution
For the purposes of this solution let’s imagine we want to add a gift note field
to all products in our store, if users choose to populate the gift note field then
we’ll display it in the cart screens and the order confirmation screens on the
front-end and it will also be available to back-end users so they can create the
gift notes as specified.

Adding the Field to the Product Screen


We’ll add our field using the ​woocommerce_before_add_to_cart_button ​action
hook

function​ ​hwn_add_custom_option​(){
$value = filter_input( INPUT_POST, ​'gift_note'​ );
​?>

106
<p>
<label ​for​=​"gift_note"​>​<?php​ _e( ​'Gift Note:'​, ​'hwn'​ ); ​?>
</label><input type=​"text"​ id=​"gift_note"​ name=​"gift_note"
placeholder=​"<?php _e( 'Enter your gift note here', 'hwn' ); ?>"
value=​"<?php $value ?>"​/>
</p>

<?php
}
add_action( ​'woocommerce_before_add_to_cart_button'​,
'hwn_add_custom_option'​);

Here’s what we’re doing in the code

● First, we check to see if the ​gift_note ​field already exists in the


pages ​POST ​variables, if it does we save the value away to a
variable named ​$value
● We then drop into HTML to output our input field, we use the
PHP ​_e ​function to ensure any non-user input text for the field is
translatable. We also populate the value of our input with any
previously found values that we discovered in the previous step.

That’s all we need to do for our input to display, the gift note field should now
be showing on our shop pages as below

107
Limiting the Custom Field To Specific Products

We can make a change to limit the gift note field to only show for specific
products by using the ​$product ​global variable that WooCommerce makes
available to us

function​ ​hwn_add_custom_option​(){
​global​ $product;

​if​ ( $product->get_id() !== 21 ) {


​return​;
}

$value = filter_input( INPUT_POST, ​'iconic-engraving'​ );


​?>

<p>
<label ​for​=​"gift_note"​>​<?php​ _e( ​'Gift Note:'​, ​'hwn'​ ); ​?>
</label><input type=​"text"​ id=​"gift_note"​ name=​"gift_note"
placeholder=​"<?php _e( 'Enter your gift note here', 'hwn' ); ?>"
value=​"<?php $value ?>"​/>
</p>

<?php
}
add_action( ​'woocommerce_before_add_to_cart_button'​,

108
'hwn_add_custom_option'​);

Here we check the product’s id and if it’s not 21 (which is the product id of the
polo shirt in our example) then we return from the function and no labels or
input fields are output to the screen.

You could obviously use different ​if l​ ogic to show the field for different
products, or groups of products.

Showing Different Inputs

If we didn’t want to show a text input then we could update the HTML code to
show a different type of input, providing the input we add writes a value
named “gift_note” to the pages ​POST ​collection, then it would work with the
remaining code in this article.

Here’s an example using a Select field

function​ ​hwn_add_custom_option​(){
global​ $product;

if​ ( $product->get_id() !== 21 ) {


return​;
}

$value = filter_input( INPUT_POST, ​'iconic-engraving'​ );


?>

<p>
<label ​for​=​"gift_note"​>​<?php​ _e( ​'Gift Note:'​, ​'hwn'​ ); ​?>
</label>
<select id=​"gift_note"​ name=​"gift_note"​>
<option value=​"love_you"​<?​=$value == ​'love_you'​ ? ​'
selected="selected"'​ : ​''​;​?>​>​<?php​ _e( ​'I love you'​, ​'hwn'​ );
?>​</option>
<option value=​"thanks"​<?​=$value == ​'thanks'​ ? ​'

109
selected="selected"'​ : ​''​;​?>​>​<?php​ _e( ​'Thanks!'​, ​'hwn'​ );
?>​</option>
<option value=​"sorry_its_late"​<?​=$value ==
'sorry_its_late'​ ? ​' selected="selected"'​ : ​''​;​?>​>​<?php​ _e( ​'Sorry
it\'s late'​, ​'hwn'​ ); ​?>​</option>
</select>
</p>

<?php
}
add_action( ​'woocommerce_before_add_to_cart_button'​,
'hwn_add_custom_option'​);

and here’s what it looks like

Just as a side note you may have noticed the logic here

<option value=​"love_you"​<?​=$value == ​'love_you'​ ? ​'


selected="selected"'​ : ​''​;​?>​>​<?php​ _e( ​'I love you'​, ​'hwn'​ );
?>​</option>

This logic will select the option that the user previously had selected prior to
page refresh, so if the user chooses “Thanks!” and then adds the product to
the cart, the “Thanks!” option will still be selected after the item has been
added to the cart.

110
The code also uses the PHP short tag for echo, which can look a little strange
if you’ve never seen it before, you can read more about it ​here​.

Validating the Custom Field


This step is not required to get everything working end-to-end but if we did
want to add validation to our gift note field we can do so by hooking into the
woocommerce_add_to_cart_validation ​filter

function​ ​hwn_gift_note_add_to_cart_validation​( $passed, $product_id,


$qty ){

$value = filter_input( INPUT_POST, ​'gift_note'​ );


if​( !$value ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( ​'%s cannot be added to the cart
until you enter a gift note.'​, ​'hwn'​ ), $product->get_title() ), ​'error'
);
return​ ​false​;
}

return​ $passed;

}
add_filter( ​'woocommerce_add_to_cart_validation'​,
'hwn_gift_note_add_to_cart_validation'​, 10, 3 );

Let’s step through the code

● To begin with, we use the WordPress ​filter_input ​function to see


if we have a value with the key of “gift-note” in the POST
variables
● We then check to see if the value is empty (​using PHP’s falsey
logic​), if you wanted to use more complicated validation logic
then you could just change the ​if ​statement to suit your needs.
● If the value is empty then we retrieve the product details using
the ​wc_get_product ​WooCommerce function, note that we can’t

111
use the ​$product ​global variable as it is not available to us in this
function. We are aware of the product id that we need to use as
WooCommerce passes it to the filter function in the ​$product_id
variable
● We then use the product title to display a message telling the
user they should add a gift note

● and pass a false value back to the caller to indicate the


validation has failed
● If the user has passed a valid value, then we do nothing and
pass back the ​$passed​ value, which was passed in by the code
that calls the filter

Note that this function would also prevent users from adding products directly
to the cart from the shop screen

112
If you wanted to allow users to continue to add products directly to the cart
without having to add a gift note then we could add a conditional check to the
if within the validation function to ensure the user is on the single product page

function​ ​hwn_gift_note_add_to_cart_validation​( $passed,


$product_id, $qty ){

$value = filter_input( INPUT_POST, ​'gift_note'​ );


if​( !$value && is_product() ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( ​'%s cannot be added to the
cart until you enter a gift note.'​, ​'hwn'​ ), $product->get_title()
), ​'error'​ );
return​ ​false​;
}

113
return​ $passed;
}
add_filter( ​'woocommerce_add_to_cart_validation'​,
'hwn_gift_note_add_to_cart_validation'​, 10, 3 );

Adding the Gift Note to the Cart Item Data

Now we have added the gift note field to the product page we need to
intercept the value input by the user and it to the cart item data.

We can do this by hooking into the ​woocommerce_add_cart_item_data​ filter.

function​ ​hwn_add_gift_note_to_cart​( $cart_item_data, $product_id,


$variation_id ) {
$gift_note = filter_input( INPUT_POST, ​'gift_note'​ );

​if​ ( e​ mpty​( $gift_note ) ) {


​return​ $cart_item_data;
}

$cart_item_data[​'gift-note'​] = $gift_note;

​return​ $cart_item_data;
}
add_filter( ​'woocommerce_add_cart_item_data'​,
'hwn_add_gift_note_to_cart'​, 10, 3 );

Here’s what we’re doing in the function

● We firstly use PHP’s ​filter_input​ function to get the “gift-note”


value key from the POST collection, this should be the value the
user entered when adding the product to the cart
● If we can’t find a value for “gift-note” then we take no further
action and return the ​$cart_item_data​ variable unchanged

114
● If we do find a value then we add a key-value pair to the
$cart_item_data ​with the key “gift-note” and the value that was
entered into the “gift-note” field
● We now return the modified ​$cart_item_data ​and the
WooCommerce core code will add it to the cart

Displaying the Gift Note Data in the Cart


Our gift note data has now been added to the cart but it won’t display
anywhere, to get it to display in the in pre checkout screen we need to hook
into the “woocommerce_get_item_data” filter

function​ ​hwn_get_gift_note_item_data​( $item_data, $cart_item ) {

if​ ( ​isset​( $cart_item[​'gift-note'​] ) ){


$item_data[] = ​array​(
'key'​ => __( ​'Gift Note'​, ​'hwn'​ ),
'display'​ => wc_clean($cart_item[​'gift-note'​])
);
}

return​ $item_data;

}
add_filter( ​'woocommerce_get_item_data'​,
'hwn_get_gift_note_item_data'​, 10, 2 );

The first thing we do in this code is to check to see if the “gift-note” key we
added via the ​woocommerce_add_cart_item_data i​ s present, if it is then we
add a new key-value pair to the ​$item_data​ array with the following values

● key – this will be the name of our item, so in this case, we use
the text “Gift Note”
● display – this is the value of our item, so we pass in the
“gift-note” value from the ​$cart_item ​array

115
We should now see the gift note value displayed on the main cart page

and the mini cart

116
Adding the Gift Note Data to an Order
Line Item
Now the data is displaying in the cart we need to get it to display in the post
order screen, we can do this by hooking into the
woocommerce_checkout_create_order_line_item a ​ ction

117
function​ ​hwn_add_gift_note_to_order_line_item​( $order_item,
$cart_item_key, $cart_item_values ) {

if​ ( ! ​empty​( $cart_item_values[​'gift-note'​] ) ) {


$order_item->add_meta_data( __( ​'Gift Note'​, ​'hwn'​ ),
$cart_item_values [ ​'gift-note'​ ]);
}

}
add_action( ​'woocommerce_checkout_create_order_line_item'​,
'hwn_add_gift_note_to_order_line_item'​, 10, 3 );

In this function, we check to see if there is a “gift-note” value in the


$cart_item_values a ​ rray, if there is a value then we add the data to the order
item metadata via the ​add_meta_data​ function. We pass the following values
to the function

● The name of the item, in this case, we pass “Gift Note”


● The value of the item, for this, we pass the “gift-note” value that
we have written to the cart item data in previous steps

The gift note value should now start to appear in all the post order screens
and emails

118
Here it is in the order email

and also in the admin “Order” screens

119
Conclusion
Often clients will ask for custom fields and logic to be added products in
WooCommerce stores, hopefully, the snippets above will allow you to add the
custom logic you require.

120
How to Programmatically
Create a Coupon
The Problem
Although WooCommerce provides an admin interface to add coupons,
sometimes we might want to create a coupon on the fly in code rather than
adding one via the admin interface.

In this article we’ll look at how to create WooCommerce coupon


programmatically, this will include a reasonably in-depth look at the various
options available to us when creating a coupon.

We’ll also look at some code that applies a programmatically created coupon
to a user’s basket.

Creating a Coupon Programmatically

The function below will create a coupon

121
function​ ​generate_coupon​($coupon_code) {

$coupon = ​new​ WC_Coupon();

$coupon->set_code($coupon_code);

//the coupon discount type can be 'fixed_cart', 'percent'


or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type(​'fixed_cart'​);

//the discount amount, defaults to zero


$coupon->set_amount(20);

//the coupon's expiration date defaults to null


$coupon->set_date_expires(​null​);

//determines if the coupon can only be used by an


individual, defaults to false
$coupon->set_individual_use(​false​);

//the individual prodcuts that the disciunt will apply to,


default to an empty array
$coupon->set_product_ids(​array​());

//the individual products that are excluded from the


discount, default to an empty array
$coupon->set_excluded_product_ids(​array​());

//the times the coupon can be used, defaults to zero


$coupon->set_usage_limit(0);

//the times the coupon can be used per user, defaults to


zero
$coupon->set_usage_limit_per_user(0);

//whether the coupon awards free shipping, defaults to


false
$coupon->set_free_shipping(​false​);

122
//the product categories included in the promotion,
defaults to an empty array
$coupon->set_product_categories(​array​());

//the product categories excluded from the promotion,


defaults to an empty array
$coupon->set_excluded_product_categories(​array​());

//whether sale items are excluded from the coupon,


defaults to false
$coupon->set_exclude_sale_items(​false​);

//the minimum amount of spend required to make the coupon


active, defaults to an empty string
$coupon->set_minimum_amount(​''​);

//the maximum amount of spend required to make the coupon


active, defaults to an empty string
$coupon->set_maximum_amount(​''​);

//a list of email addresses, the coupon will only be


applied if the customer is linked to one of the listed emails,
defaults to an empty array
$coupon->set_email_restrictions(​array​());

//save the coupon


$coupon->save();

return​ $coupon_code;
}

You’ll notice that I’ve included a lot of calls to “set_” functions, and in many
cases, I’ve passed the default value to the function so the call has no real
effect, I’ve only added the calls to illustrate what fields you can set if you need
to.

You could create a coupon using the much shorter function below

123
function​ ​generate_coupon​($coupon_code) {

$coupon = ​new​ WC_Coupon();

$coupon->set_code($coupon_code);

//the coupon discount type can be 'fixed_cart', 'percent'


or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type(​'fixed_cart'​);

//the discount amount, defaults to zero


$coupon->set_amount(20);

//save the coupon


$coupon->save();

return​ $coupon_code;
}

and then just add in any “set_” functions that you need to use.

Adding the Programmatically Created


Coupon to a User’s Basket

Here’s the code

124
add_action(​'woocommerce_before_calculate_totals'​,
'hwn_add_programmatically_created_coupon_to_basket'​);
function​ ​hwn_add_programmatically_created_coupon_to_basket​(
$cart ) {
​if​ ( is_admin() &amp;&amp; ! defined( ​'DOING_AJAX'​ ) )
​return​;

​if​ ( did_action( ​'woocommerce_before_calculate_totals'​ ) >= 2


)
​return​;

$coupon_code = ​'hwn_generated_coupon'​;

​if​ (!coupon_exists($coupon_code)) {
generate_coupon($coupon_code);
}

$applied_coupons = $cart->get_applied_coupons();

​ f​( ! in_array($coupon_code, $applied_coupons) &amp;&amp;


i
cart_contains_items(​'beanie'​ )){
$cart-&gt;add_discount( $coupon_code );
wc_clear_notices();
wc_add_notice(__(​"Your order contains a beanie so a
discount has been applied!"​, ​"woocommerce"​), ​"notice"​);
}
​elseif​( in_array($coupon_code, $applied_coupons) &amp;&amp;
!cart_contains_items(​'beanie'​ )){
$cart->remove_coupon( $coupon_code );
}
}

In this code, we create a function and hook it into the


“woocommerce_before_calculate_totals” action. Let’s take a look at what the
function does.

● We firstly add some code to exit the function if it has been called
via the admin area or if an AJAX action is taking place

125
● We then ensure that the
“woocommerce_before_calculate_totals” action has been called
no more than two times previously using the WordPress
did_action​ function
● Now we create a name for our coupon and store it in the
$coupon_code ​variable
● We now need to check if our coupon has already been created,
we do this using a custom function named ​coupon_exists ​we’ll
take a look at this function in more detail shortly.
● If a coupon exists with the same name as our ​$coupon_code
variable then we don’t create a new coupon, if there is no
coupon already in existance then we call the ​generate_coupon
we defined earlier. If your logic is different, e.g. you need to
create a coupon for every customer, then you could change this
logic to suit your needs
● Now we have a coupon ready to use we get the coupons already
applied to the user’s basket, we do this by using the
WooCommerce built-in ​$cart->get_applied_coupons() f​ unction,
we write this information to an array named ​$applied_coupons
● We now exercise some logic to see if we should apply the
coupon to the basket, when you’re implementing the code in
your own stores you may just want to apply the coupon to every
basket or set-up the qualifying logic in the coupon itself, but for
this example, I’ve introduced some ​if​ logic just to show what
happens when the coupon is or isn’t applied to the basket.
● In order to provide the ​if ​logic I’ve added a custom function
named ​cart_contains_items ​that returns true if a product is in the
basket, specifically we fire the coupon add logic if the basket
contains a product with a product slug of “beanie”.
● If the cart does include a beanie then we call the
$cart->add_discount( $coupon_code ) ​to add the coupon to the
basket, we then clear the notices and add a new notice informing
the customer that the discount has been applied

126
● Finally, the ​elseif l​ ogic at the end of the function handles the
case where a user doesn’t have a beanie in their basket, here
we call the ​$cart->remove_coupon( $coupon_code ) ​ to remove
the coupon from the user’s basket. This code deals with the
scenario where the user has had a beanie in their basket but has
removed it, calling ​remove_coupon ​ ensures that a user is no
longer awarded the coupon after the Beanie has been removed.

The coupon_exists and


cart_contains_items functions
When we looked at the code we mentioned a couple of custom functions that
we used, we’ll take a look at these functions now. We’ll start with the
coupon_exists​ function

127
function​ ​coupon_exists​($coupon_code) {
global​ $wpdb;

$sql = $wpdb->prepare( ​"SELECT post_name FROM $wpdb->posts


WHERE post_type = 'shop_coupon' AND post_name = '%s'"​,
$coupon_code );

$coupon_codes = $wpdb->get_results($sql);

if​ (count($coupon_codes)> 0) {
return​ ​true​;
}
else​ {
return​ ​false​;
}
}

In this function, we use the $wpdb global object to query the WordPress
database to see if we already have a coupon code with the coupon code we
wish to use.

Let’s step through the code

● We firstly create a SQL statement that queries the database to


see if it can find a record in the ​posts ​table with a ​post_type ​of
“shop_coupon” and the name that we supply.
● Note how we pass the SQL statement to the ​$wpdb->prepare
function to protect ourselves from a ​SQL injection attack​.
● We then use the ​$wpdb->get_results f​ unction to return an array
of the results returned by the SQL query
● If the array is empty then we return false (indicating the coupon
does not already exist), if the array contains any results then we
return true

We can now use this function to determine if a coupon exists, and only create
a new coupon if we do not find one in the database.

128
Finally, let’s take a look at the ​cart_contains_items f​ unction

function​ ​cart_contains_items​( $search_products ) {


$searching_for_single_product =
!is_array($search_products);

if​ ( ! WC()->cart->is_empty() ) {
// Loop though cart items
foreach​(WC()->cart->get_cart() ​as​ $cart_item ) {
// Handling also variable products and their
products variations
$products_ids_array[] =
$cart_item[​'product_id'​];
$products_ids_array[] =
$cart_item[​'variation_id'​];
$products_ids_array[] =
$cart_item[​'data'​]->get_slug();
}
}

if​ ($searching_for_single_product) {
return​ in_array($search_products,
$products_ids_array);
}
else​ {
return​ !array_diff($search_products,
$products_ids_array);
}
}

I’m not going to go through this line by line as it’s covered in the “How to
Check If an Item Is in a User Cart” chapter.

129
How to Check a Products Type
The Problem
As WooCommerce developers we may sometimes need to check on a
product’s type before we run some custom logic.

In this chapter we’ll take a look at how we can check what type a product is.

The Solution
In order to get a product’s type we firstly need a product object, we showed
how to do this in the “How to Get A Products Details Using a Product Id”
chapter so you should check out that chapter if you’re not sure how to get hold
of a product object.

Once we have a product object we can call the ​get_type ​function

$product->get_type()

The function should return one of the following values -

simple​ - For simple products


variable - ​For variable products
grouped - ​For grouped products
external - ​For external products

If you want to check if a product is a certain product type then you can use the
is_type function using one of the values above

130
$product->is_type(​'simple'​) //is a simple product
$product->is_type(​'variable'​) // is a variable product
$product->is_type(​'grouped'​) //is a grouped product
$product->is_type(​'external'​) //is an external product

Should I use get_type() or is_type()


This is really a matter of personal preference, if you wanted to check if a
product was of a certain type, using ​get_type()

if​ ($product->get_type() == ​"simple"​) {


​//logic goes here
}

or using is_type()

if​ ($product->is_type(​"simple"​)) {
​//logic goes here
}

Should give you the same results.

Final Notes
We can use the built in product functions ​get_type ​and​ is_type ​to ascertain a
product’s type.

131
How to Get A Products Details
Using a Product Id
The Problem
When we are developing WooCommerce functionality we will sometimes need
to check the details of a product.

To do this we’ll need to create a WooCommerce product object, we can then


call functions on the object to find out information about the product.

If you’re trying to get product details then you’re most likely in one of two
different scenarios we’ll look at each of the scenarios in this chapter.

Scenario 1 - We are on A Product Page, or a


Page that Displays Lists of Products
This scenario would apply to you if your code is running on a product page or
a page that displays products.

Because of the way that WooCommerce works this scenario is likely to apply
if you’re hooking into an action or filter that fires on one of the product or
catalog pages.

As an example we’ll use the “woocommerce_before_add_to_cart_button”


action that fires on the single product page.

function​ ​hwn_get_product_information​() {
​global​ $product;

132
​echo​ ​"The product name is "​ . $product->get_title() . ​" and it's
id is "​ . $product->get_id() . ​"."​;
}
add_action( ​'woocommerce_before_add_to_cart_button'​,
'hwn_get_product_information'​);

In this example we are able to use the ​$product​ global variable, this is
something that WooCommerce makes available to us and we can use it to
access product information ​without​ an id.

We get access to the global variable on this line of the code

global​ $product;

​ lobal variable will give us access to a product’s


In most cases the ​$product g
details and we won’t need the product’s id.

Here’s the output from the function above

​ lobal Variable is not Available?


How do I Know if the ​$product G

The ​$product ​global variable should be available on most customer facing


shop pages, you can check if the ​$product ​global variable has been populated
using the following code.

if​ ($product === ​null​) {


//$product is not available
}

133
Scenario 2 - We only have Access to the
Product Id
If we don’t have access to the global $product variable, but we do know the id
of the product we need the details from, then we can use the ​wc_get_product
function to get the product details.

As an example, we’ll revisit a function we used in the “How to Add a Custom


Field to a Product” chapter.

function​ ​hwn_gift_note_add_to_cart_validation​( $passed,


$product_id, $qty ){

$value = filter_input( INPUT_POST, ​'gift_note'​ );


if​( !$value && is_product() ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( ​'%s cannot be added to the
cart until you enter a gift note.'​, ​'hwn'​ ), $product->get_title()
), ​'error'​ );
return​ ​false​;
}

return​ $passed;
}
add_filter( ​'woocommerce_add_to_cart_validation'​,
'hwn_gift_note_add_to_cart_validation'​, 10, 3 );

The function above is called when WooCommerce is validating a user's cart


content, so it does not have access to the global $product variable.

However, WooCommerce does pass a ​$product_id v​ ariable to the filter so we


use that and the ​wc_get_product ​function to get our product details.

Here’s the code we can use to get a product’s details using only an id

134
$product_id = 33;
$product = wc_get_product( $product_id );
echo​ ​"The product name is "​ . $product->get_title() . ​" and it's
id is "​ . $product->get_id() . ​"."​;

Final Notes
If we want to access a product’s details in many cases we’ll be able to access
them using the ​$product ​global variable.

If we only have access to a product’s id then we can retrieve it’s details using
the ​wc_get_product​ function.

135
How to Access the Details of all
The Products in a Users Basket
The Problem
As Woocommerce developers we will often need to check the contents of a
user's basket. In this chapter we’ll take a look at how to access a user’s
basket in code and also how to loop through the products in a user’s basket
and find further details about each product.

The Solution
In Woocommerce it is is possible to access the current users basket using the
following code

WC()->cart

This will return a reference to the current users cart, there are a number of
different methods we can call from this object, but as we’re looking at
searching through the products in a user’s cart in this chapter we’ll be using
the

WC()->cart->get_cart()

function. We can use this function alongside a php foreach loop in order to
access all of the products in a user’s cart. Here’s the code we will use

foreach​(WC()->cart->get_cart() ​as​ $cart_item) {


​// Add code here to get values from and/or change cart item data
}

136
As you can see from the code above we use the foreach method to iterate (or
loop over) each array element in the array that is returned by the ​get_cart
function, we are then able to access each element inside the squiggly
brackets that make up the body of the ​foreach ​loop.

Let’s take a look at some code which will hopefully make things clearer

foreach​(WC()->cart->get_cart() ​as​ $cart_item) {


//the number of items in this cart item
$quantity = $cart_item[​'quantity'​];

//the product id, if the cart item contains a


variable product this will be the id for the parent product
$product_id = $cart_item[​'product_id'​];

//the variation id, if the cart item contains


a non variable product this will return 0
$variation_id = $cart_item[​'variation_id'​];

//the cost of this cart item before any


discounts are applied
$line_subtotal = $cart_item[​'line_subtotal'​];

//the amount of tax charged on the cart item


before discounts are applied
$line_subtotal_tax =
$cart_item[​'line_subtotal_tax'​];

//the cost of this cart item after any


discounts have been applied
$line_total = $cart_item[​'line_total'​];

//the amount of tax charged on the cart item


after any discounts have been applied
$line_total_tax =

137
$cart_item[​'line_total_tax'​];

//an array containing the variable data for


the product linked to the cart line item
//if the cart item contains a non variant
product then this will be an empty array
//an example of what this array may contain
"attribute_pa_color" => "blue", "attribute_pa_size" =>
"large"
$variation = $cart_item[​'variation'​];

//the key of the cart item, this uniquely


identifies the cart item and can be passed as an argument in
to cart related functions
//e.g.wc_get_cart_remove_url( $key )
$key = $cart_item[​'key'​];

//the value contained in the "data" element,


is a Woocommerce product object that contains details about
the product
//that is linked to the cart item, we can call
product object methods on this object, see the example below
$product = $cart_item[​'data'​];

//an example of how we can access product data


$productname = $product->get_name();

Rather than go through each property in the text I’ve added comments that
describe the properties we have accessed in the loop. As you can see, there
is a lot of data available to us about each cart item.

138
Different ways of writing the foreach
statement
It’s possible that you may have seen the cart ​foreach ​loop written in a couple
of different ways, in the example above we used this code

foreach​(WC()->cart->get_cart() ​as​ $cart_item) {


​// Add code here to get values from and/or change cart item
data
}

But you can also write the ​foreach ​statement like this

foreach​(WC()->cart->get_cart() ​as​ $cart_key => $cart_item) {


​// Add code here to get values from and/or change cart item data
}

Doing it this way provides exactly the same functionality as the first way, but
as it uses the key/value method of looping over the array the key of each cart
item will be assigned to the ​$cart_key ​variable, the ​$cart_item​ variable would
contain the same data in both examples.

Note that the value of ​$cart_key ​variable will be the same as the value
returned by calling ​$cart_item['key'].​

Dealing with Empty Carts


If the ​foreach ​statement is called on an empty cart, then the loop won’t have
anything to process and nothing will happen.

139
So no special code is required to handle an empty cart, it is possible to check
if the cart is empty before calling the ​foreach s​ tatement though, as shown in
the example below

if​ ( ! WC()->cart->is_empty() ) {
​foreach​(WC()->cart->get_cart() ​as​ $cart_key => $cart_item)
{
​// Add code here to get values from and/or change cart item
data
}
}

You may wish to use code like this if you want to run additional logic or show
extra content if the basket is empty.

Some Example Code that Displays Cart Data


Now that we have looked at the various ways to access the products in a
user’s basket, let’s write some example code to list the details of a user’s
basket.

For our example, we’ll list the name and quantity of each item in the basket
and also provide a link to remove the item from the basket.

Here’s our code

foreach​(WC()->cart->get_cart() ​as​ $cart_key => $cart_item) {


$quantity = $cart_item[​'quantity'​];
$productname = $cart_item[​'data'​]->get_name();
​echo​ $productname . ​" (Qty - "​ . $quantity . ​") <a href='"​ .
wc_get_cart_remove_url( $cart_key ) . ​"'>Remove Item From
Cart</a><br>"​;
}

140
In the code we firstly extract the quantity of the line item and we then get the
product name. Note how we are able to call a product method directly from the
$cart_item['data'] ​array​ ​element, without assigning the product object to a
variable first.

We then echo these values alongside a link we build up using the url returned
by the ​wc_get_cart_remove_url ​method.

Here’s what get’s output to the screen after the code has run

141
How to Get User Details from the
Cart
The Problem
In the “How to Check If an Item Is in a User Cart” chapter we took a look at
how to work out if a particular product is in a user’s basket. But what if we
want to get the user's details for the cart object? In this chapter we’ll find out
how.

The Solution
WooCommerce makes it very easy for us to get customer’s details from a cart
​ ethod on the cart object
object by providing a ​get_customer m

$customer_details = WC()->cart->get_customer();

The method will return a WC_Customer object regardless of whether the cart
belongs to a logged in or logged out user, you can quickly check if the cart
belongs to a logged in user by checking to see if the id property of the
customer object is greater than zero. Here’s some sample code

$customer_details = WC()->cart->get_customer();
if​ ($customer_details->get_id() > 0) {
​echo​ ​"The cart belongs to user "​ .
$customer_details->get_first_name() . ​" "​ .
$customer_details->get_last_name();
}
else​ {
​echo​ ​"The cart belongs to a logged out user."​;
}

142
If we were to add the code above to a page similar to the one we created in
the “How to Add a PHP Page to WooCommerce” chapter, we would see the
following output for a logged out user

and this for a logged in user

​ ethod will return the details of the logged in user


The cart’s ​get_customer m
regardless of whether they have items in their cart. So the output above would
show for empty and non-empty carts.

143
 

How to Check If an Item Is in a


User Cart
The Problem
When we are adding functionality to a WooCommerce store, we will often
need to check what the user has in their cart before we choose to fire a piece
of functionality such as a discount or a user message.

In this chapter we’ll take a look at a couple of methods to check if a user has a
product in their cart.

The Solution
In order to provide solutions we will rely heavily on the code we discussed in
the “How to Access the Details of all The Products in a Users Basket” chapter.

Let’s take a look at a function that will return true or false if a user has a
number of item(s) in their basket

function​ ​cart_contains_items​( $search_products ) {


$searching_for_single_product = !is_array($search_products);

​if​ ( ! WC()->cart->is_empty() ) {
​// Loop through cart items
​foreach​(WC()->cart->get_cart() ​as​ $cart_item ) {
$products_ids_array[] = $cart_item[​'product_id'​];
$products_ids_array[] = $cart_item[​'variation_id'​];
$products_ids_array[] = $cart_item[​'data'​]->get_slug();

144
}
}

​if​ ($searching_for_single_product) {
​return​ in_array($search_products,
$products_ids_array,​true​);
}
​else​ {
​return​ !array_diff($search_products, $products_ids_array);
}
}

The function will accept either a single argument or an array of arguments that
can be either a product id, a variant id or a slug (e.g. “belt”). The function will
then return true or false if all of the passed ids are in the current user’s basket.

Here are a few ways that we could call the function

cart_contains_items(16);
cart_contains_items(​'belt'​);
cart_contains_items(​array​(16,​'belt'​));

The function begins by checking if the passed argument is an array, it then sets a 
flag named ​$searching_for_single_product w​ hich we will use later on in the function. 

Next, the function loops through the products in the cart and builds up an array that 
contains all of the product ids, variation ids, and slugs of the products in the basket. 

The function then checks the $​ searching_for_single_product v​ ariable, if it’s true then 
we return the result of the PHP i​ n_array​ function, we pass the following arguments to 
the ​in_array​ function 

● $search_products t​ his is the single argument that was passed to the function 
so it will contain either a product id, a variation id or a slug 

145
● $products_ids_array ​this is the array we built up in the previous step
when we looped through the products in the basket
● true t​ his is an optional parameter that tells the ​in_array​ function to do a strict 
comparison of each value it compares, this ensures that strings are compared 
to strings and integers are compared to integers. PHP can sometimes return 
unexpected results when we do not use strict comparison rules so we need 
to specify true for this parameter 

The ​in_array​ function will now return a value of true if it finds the argument that was 
passed to the function in the ​$products_ids_array, i​ t will return false if it can’t
find the value in the array.

This functionality will ensure we return the correct value if we are only
checking for a single value.

If we are checking for multiple values then we use the PHP ​array_diff​ function,
this function takes multiple arrays as arguments. It compares the first array
passed to it against all the other arrays passed as arguments and then returns
any values that were in the first array but not present in the other arrays
passed to the function.

In our function we pass the ​$search_products ​array passed to the function as


the first argument and then the ​$products_ids_array ​that contains all of the
relevant ids from the basket as the second argument.

If all the items we are searching for are contained in the basket then we would
expect the ​array_diff​ function to return nothing, as there will be no elements
from ​$search_products t​ hat are not present in the ​$products_ids_array.

We then use this line of code

return​ !array_diff($search_products, $products_ids_array)

146
This is pretty confusing, but you might see code similar to this in the wild so
we’ll go through it here.

If the ​array_diff ​function returns nothing then PHP sees this value as false as it
equates nothing with falseness.

But in this case we don’t want to directly return false as, if the ​array_diff
function returns false it means that we have found all of the items we were
searching for in the basket. So we add the ! operator which is a shorthand way
of writing

return​ array_diff($search_products, $products_ids_array) ==


false

Another Solution
There are multiple ways to check if products are present in a user’s basket
and you can hopefully create your own based on what you’ve learnt so far, but
let’s take a look at another function that counts the quantities of items in a
user’s basket before closing out this chapter.

Here’s the function

function​ ​get_cart_quantities​( $search_products ) {


​if​ (!is_array($search_products)) {
$search_products = ​array​($search_products);
}
$total_quantity = 0;

​if​ ( ! WC()->cart->is_empty() ) {
​// Loop though cart items
​foreach​(WC()->cart->get_cart() ​as​ $cart_item ) {
​if
(in_array($cart_item[​'product_id'​],$search_products,​true​) ||

147
in_array($cart_item[​'variation_id'​],$search_products,​true​) ||
in_array($cart_item[​'data'​]->get_slug(),$search_products,​true​)) {
$total_quantity = $total_quantity +
$cart_item[​'quantity'​];
}
}
}

​return​ $total_quantity;
}

The function above behaves in a similar fashion to our first function in that it
will accept either a single argument or an array of arguments that can be
either a product id, a variant id or a slug. The function is different from our
previous function in that it will return the total quantity of all products it finds in
the basket rather than true or false depending on whether it finds all the
products passed in to the function in the current basket.

We begin this function by checking if the value passed is an array or not, if the
value is not an array then we convert to an array, as that will make it easier to
execute our logic.

The function then loops through items in the cart and uses the ​in_array
function that we looked at in our previous example to check if the product in
the basket has either a product id, variant id or slug that is included in our
array of products to search for.

If the function finds a match then it increments the ​$total_quantity v​ ariable


(that is initialized to zero at the start of the function) by the quantity of the
matching products in the basket.

The function then returns the value of the ​$total_quantity ​variable after it has
finished looping over all of the products in the cart.

148
How to Add a Surcharge Based
on a Custom Field
The Problem
In the “How to Add a Custom Field to a Product” chapter we took an in-depth
look at how to add a custom field to a product when we added a “Gift Note”
field to products in our store.

In this chapter we will take a look at how to add a surcharge based on a


custom field.

The Solution
For the purposes of this chapter we’re going to assume that you have all the
code from the “How to Add a Custom Field to a Product” chapter already in
place.

What we’re going to do is add a surcharge if a gift note has been added and
then inform the user of the surcharge on the cart and checkout pages.

Let’s take a look at the code required to add the fee

function​ ​hwn_add_gift_note_custom_fee​( $cart ) {


​if​ ( is_admin() && ! defined( ​'DOING_AJAX'​ ) ) ​return​;
$fee = 5;

​foreach​(WC()->cart->get_cart() ​as​ $cart_item ) {


​if​ ($cart_item[​'gift-note'​]) {
$cart->add_fee( __( ​"Gift note"​, ​"woocommerce"​ ), $fee,
false​ );

149
}
}
}
add_action( ​'woocommerce_cart_calculate_fees'​,
'hwn_add_gift_note_custom_fee'​, 10, 1 );

As you can see from the code above we add the fee by hooking into the
“woocommerce_cart_calculate_fees” action using a function named
“hwn_add_gift_note_custom_fee”.

The first thing we do in the function is to check that the code is not being
called in the Wordpress admin interface or via an AJAX call.

We then set the variable ​$fee ​with the value that we want to charge for the gift
note, in this case we set the fee to 5.

The function then loops over all of the items in the cart, if it finds an item that
has a gift note (it checks the ​$cart_item['gift-note'] ​value to ascertain this)
then it adds a fee to the cart via the ​$cart->add_fee ​function. This function
takes the following values -

● Name​ - The name of the fee, this will be shown on the cart and
checkout pages, it is not possible to add multiple fees to the cart with
same name
● Amount - ​The amount that will be charged as a fee
● Taxable -​ A true or false value that indicates whether the fee is taxable,
we set this to false in the example above, but this field is optional and
defaults to false, so we would have got the same results by omitting this
field.
● Tax class​ - A tax class that applies to the field if it’s taxable

Once the code above has been added the fee should start showing on the cart
and checkout pages

150
Adding Multiple Charges
Because the ​$cart->add_fee f​ unction will only add one fee per label the code
above will only charge a customer once regardless of how many items they
have in their baskets with gift notes. If we wanted to charge the customer for
each item they had in their basket with a gift note the we would need to
change the code to something like this

function​ ​hwn_add_gift_note_custom_fee​( $cart ) {


​if​ ( is_admin() && ! defined( ​'DOING_AJAX'​ ) ) ​return​;
$fee = 5;
$total_fee = 0;

​foreach​(WC()->cart->get_cart() ​as​ $cart_item ) {


​if​ ($cart_item[​'gift-note'​]) {
$total_fee = $total_fee + ($fee *
$cart_item[​'quantity'​]);
}
}
$cart->add_fee( __( ​"Gift note"​, ​"woocommerce"​ ), $total_fee,
false​ );
}
add_action( ​'woocommerce_cart_calculate_fees'​,

151
'hwn_add_gift_note_custom_fee'​, 10, 1 );

This code keeps a running total of the combined gift note fee whilst we are
looping around the items in the basket, it then adds the total fee once the loop
has completed.

Note that we also have to multiply the fee by the quantity for each basket item
to cover any cases where a user has ordered multiple items with the same gift
note.

Charging Different Fees


If we wanted to charge customers different amounts depending on the length
of their gift note we could do that by making modifications to the ​if ​statement in
the cart item’s loop, here’s the code we could use.

function​ ​hwn_add_gift_note_custom_fees_based_on_gift_note_length​(
$cart ) {
​if​ ( is_admin() && ! defined( ​'DOING_AJAX'​ ) ) ​return​;
$total_fee = 0;

​foreach​(WC()->cart->get_cart() ​as​ $cart_item ) {


​if​ ($cart_item[​'gift-note'​]) {
$fee = 5;
​if​ (strlen($cart_item[​'gift-note'​]) >=10) {
$fee = 10;
}
$total_fee = $total_fee + ($fee *
$cart_item[​'quantity'​]);
}
}
$cart->add_fee( __( ​"Gift note"​, ​"woocommerce"​ ), $total_fee,
false​ );
}
add_action( ​'woocommerce_cart_calculate_fees'​,

152
'hwn_add_gift_note_custom_fees_based_on_gift_note_length'​, 10, 1
);

Hopefully these last two examples will give you some ideas of how you could
alter the code to suit your own future requirements.

153
The Checkout
Having looked at the purchasing process and the cart in the last section, we’ll
now take a look at how to change the checkout process to suit your needs.

154
How to change Labels on the
Checkout Screen
The Problem
WooCommerce provides a ready made checkout screen, but clients will often
request changes to the labels on the fields in order to make them specific to
their business.

In this chapter we’ll take a look at how we can change labels on the checkout
screen.

The Solution
Let’s imagine we’ve been asked to change the “Company name” label to
“Your company”

We can make the changes via the “woocommerce_billing_fields” filter

function​ ​hwn_change_company_label​($fields) {

155
$fields[​'billing_company'​][​'label'​] = ​'Your company'​;
​return​ $fields;
}
add_filter(​'woocommerce_billing_fields'​,​'hwn_change_company_label'
);

Here we create a function named “hwn_change_company_label” and hook it


into the “woocommerce_billing_fields” filter, WooCoomerce passes an array
of the checkout fields into the filter so we need to find the field we wish to
change, which is “billing_company” in this case, we then target the “label” key
of the “billing_company” array and change it’s value to the value of our choice.

The change should then be displayed on the Checkout screen

156
How to change Placeholders on
the Checkout Screen
The Problem
When we build forms on the web for users to input data the HTML spec allows
us to add placeholders which are often used to hint at the values that a user
should input.

Here’s an example of a placeholder being used in the “Order notes” in the


WooCommerce checkout process

But what if we wanted to add a placeholder to an existing checkout field or


change a placeholder, in this chapter we’ll find out how to do that.

The Solution
Let’s imagine we’ve been asked to add a placeholder to the “Company name”
field in the checkout billing details that reads “Your company name”.

By default the “Company name” does not contain a placeholder value

157
As in the “How to change Labels on the Checkout Screen” we can make the
changes via the “woocommerce_billing_fields” filter

function​ ​hwn_change_company_placeholder_billing​($fields) {
$fields[​'billing_company'​][​'placeholder'​] = ​'Your company name'​;
​return​ $fields;
}
add_filter(​'woocommerce_billing_fields'​,​'hwn_change_company_placeh
older_billing'​);

In the code above we create a function named


“hwn_change_company_placeholder_billing” and hook it into the
“woocommerce_billing_fields” filter, WooCoomerce passes an array of the
checkout fields into the “woocommerce_billing_fields” filter so we need to find
the field we wish to change, which is “billing_company”.

We then target the “placeholder” key of the “billing_company” array and


change it’s value to the value to the value of our choice.

The change should then be displayed on the Checkout screen

158
159
How to Change the Order of
Fields on the Checkout Screen
The Problem
The checkout screen in WooCommerce displays fields in a default order as
below

160
But we might want to change the order of the fields to suit our own ordering
requirements. In this chapter we’ll take a look at how to reorder the fields on
the checkout screen.

The Solution
Much like when we changed the label text and placeholders in previous
chapters we can change the order of fields via the
“woocommerce_billing_fields” filter by assigning a value to the “priority” field of
each checkout element.

Before we can look at changing priorities though, let’s take a brief detour to
find out how we can see the order of the existing elements.

We’ll do this with the code below

function​ ​hwn_show_order_of_billing_fields​($fields) {
​foreach​ ($fields ​as​ $key => $value) {
​echo​ ​"$key ({$value['priority']})<br>"​;
}
​return​ $fields;
}
add_filter(​'woocommerce_billing_fields'​,​'hwn_show_order_of_billing
_fields'​);

In the code above we hook a function named


“hwn_show_order_of_billing_fields” to the “woocommerce_billing_fields” filter.

In the function we use PHP’s ​foreach f​ unctionality to loop through the array of
fields that is passed to the filter, we then output the field name (which is the
key value of each array element) alongside the “priority” value for that
element.

161
We get the “priority” value by looking up the “priority” key in the array that is
assigned to the ​$value ​variable by PHP as it loops through the ​$fields a​ rray.

Note also that even though we’re not changing the ​$fields​ array we still need
to return it at the end of the function so code that has called the filter get’s a
value returned to it. This is standard practice when we are hooking into any
filter in WooCommerce or Wordpress.

The function should output something similar to the output below to the screen

Here we can see the priorities for all the billing fields listed, note that this list
may be output multiple times depending on how many times your theme calls
the filter. I would imagine you wouldn’t want to use the code in production
anyway, we’re just using it here for illustrative purposes.

162
Now we have the priorities, we can work out how to make a change, for the
purposes of this chapter we’ll move the “billing_company” field above the
“billing_first_name” and “billing_last_name” fields.

If we look in the priorities in the list we can see that the company field has a
priority of 30 and the two name fields have priorities of 10 and 20. So, if we
give the company field a priority that is less than 10, it should move the
company field above the two name fields.

Let’s take a look at the code

function​ ​hwn_move_company_name_above_name_fields​($fields) {
$fields[​'billing_company'​][​'priority'​] = 5;
​return​ $fields;
}
add_filter(​'woocommerce_billing_fields'​,​'hwn_move_company_name_abo
ve_name_fields'​);

As in previous examples we hook into the “woocommerce_billing_fields” filter


and change the value of a key value pair inside the array that corresponds to
the field value we want to change.

In this case we change the “priority” value inside the “billing_company” array
to 5, which makes it lower than the two values set for the first and last name
checkout fields.

When the code is run the order of the checkout fields is changed, as you can
see in the screenshot below

163
164
How to Remove a Field on the
Checkout Screen
The Problem
In previous chapters we have looked at how to change labels, placeholders
and the order of fields on the checkout screen, but what if we want to remove
a field entirely?

In this chapter we’ll take a look at how to remove the “company name” field
(as seen below) completely from the checkout screen.

The Solution
As in previous chapters, we can use the “woocommerce_billing_fields” filter to
accomplish our goal, but this time, instead of updating a value in the array that
is passed to the filter we will remove an element from the array entirely, let’s
take a look at the code to do this

function​ ​hwn_remove_company_name_from_billing_fields​($fields) {

165
​unset​($fields[​'billing_company'​]);
​return​ $fields;
}
add_filter(​'woocommerce_billing_fields'​,​'hwn_remove_company_name_f
rom_billing_fields'​);

In the code above we create a function named


“hwn_remove_company_name_from_billing_fields” and hook it into the
“woocommerce_billing_fields” filter.

Inside the function we use PHP’s ​unset ​function which removes an item from
an array, the ​unset f​ unction takes an array item as an argument, so in our
case we specify the billing company element of the ​$fields a ​ rray using the
“billing_company” key.

The element is then removed from the array, and we return the ​$fields ​ array
back to the code that called the filter.

The company name is then removed from the billing address as we can see in
the screenshot below.

166
How to Make a Field Required on
the Checkout Screen
The Problem
When designing a checkout process we need to consider what data we need
from the user, some of this data will be “nice to have” but other data will be
essential for us to be able to complete a cusomer’s order.

By default, WooCommerce sets a number of checkout fields as “required” . In


this chapter we’ll take a look at how we can set a field to be optional or
required within the checkout process.

The Solution
Throughout this section, we’ve been using the billing “Company Address” field
as an example so we’ll use it again to show how to make a field required.

We will also hook into the “woocommerce_billing_fields” filter again to


accomplish our task. Let’s look at the code we’ll need to use

function​ ​hwn_change_company_billing_field_to_be_required​($fields)
{
$fields[​'billing_company'​][​'required'​] = ​true​;
​return​ $fields;
}
add_filter(​'woocommerce_billing_fields'​,​'hwn_change_company_billin
g_field_to_be_required'​);

167
In the code above we create a function named
“hwn_change_company_billing_field_to_be_required” and hook it into the
“woocommerce_billing_fields” filter.

Inside the function we change the “billing_company” element of the ​$fields


array by altering the value associated with the “required” key to true.

We then return our altered array to the code that called the filter.

Having made the change, WooCommerce will now indicate that the billing
company field is required on the checkout screen

It will also prompt the user if they do not fill in the “Your company” field and
attempt to complete the checkout process

168
How to Add a Field to the Billing
Address on the Checkout Screen
The Problem
WooCommerce comes with a ready made checkout screen that includes a
number of default fields in the billing address.

However we may want to add extra fields to the billing address, in this chapter
we’ll take a look at how to do that.

The Solution
Just like everything else we’ve looked at so far in this section we can add an
extra field to the billing address via the “woocommerce_billing_fields” filter.

Let’s imagine we want to add a field for the user to add their mobile number,
here's the code we’ll hook up to the filter.

function​ ​hwn_add_mobile_phone_billing_field​( $fields ) {


$fields[​'billing_mobile_phone'​] = ​array​(
​'label'​ => __(​'Mobile Phone'​, ​'woocommerce'​),
​'placeholder'​ => _x(​'Mobile Phone'​, ​'placeholder'​,
'woocommerce'​),
​'required'​ => ​false​,
​'class'​ => ​array​(​'form-row-wide'​)
);
​return​ $fields;
}
add_filter( ​'woocommerce_billing_fields'​ ,
'hwn_add_mobile_phone_billing_field'​ );

169
In the “hwn_add_mobile_phone_billing_field” function we add a new element
to the ​$fields ​array that is passed to the function via the filter.

We give the element a key of “billing_mobile_phone”, and assign a key value


array to the element.

We then supply values for the “label”, “placeholder” and “required” fields, all of
which we have already covered in this chapter.

Finally we supply a value for the “class” field, this is the CSS class that
WooCommerce will add to our new element. In this example we supply the
class “form-row-wide” as that is the class used by WooCommerce for other
fields on the checkout screen.

We then return the amended ​$fields​ array so it can be used by the code that
triggered the filter.

And that’s all we need to do, our field should now start showing on the
checkout screen

170
How to View the New Field and it’s Value on
The Order Admin Screen
Now we have added a new field we really need to be able to see the value
entered by the user during checkout in the admin interface, if you’ve skipped
ahead to the “How to Add a New Field to the Checkout Screen” chapter where
we add a custom field to the checkout you maybe expect that we’ll firstly need
to add a function that hooks up to an action in order to save our new value.

Happily for those of you that don’t like writing code, we don’t need to do this.
WooCommerce will handle the saving of the value for us due to the clever way
it loops over the fields in the billing address when it is saving the customer’s
details during the checkout process.

So, all we need to do is write some code to show our new field in the admin
screen, we can do this by hooking a function into the
“woocommerce_admin_order_data_after_billing_address” action that fires
after WooCommerce has displayed the existing billing address details on the
admin order screen.

function
hwn_show_users_billing_mobile_number_after_billing_address​($order)
{
​echo​ ​'<p><strong>'​.__(​'Billing Mobile Phone'​).​':</strong> '​ .
get_post_meta( $order->get_id(), ​'_billing_mobile_phone'​, ​true​ ) .
'</p>'​;
}
add_action( ​'woocommerce_admin_order_data_after_billing_address'​,
'hwn_show_users_billing_mobile_number_after_billing_address'​, 10,
1 );

To understand this code we need to understand what WooCommerce does


when it saves our new field during the checkout process. Here’s a quick
summary

171
● It loops through all the fields in the billing fields address array
● If it finds a field that it knows how to save, it uses its own logic to save
the value away to the appropriate place
● If it finds a field that it does not know about, and our new mobile phone
field fits into this category, then it save the fields value into the order’s
metadata using the key we supplied when we added it to the billing
fields array (“billing_mobile_phone”) prefixed by an underscore so
“_billing_mobile_phone” in this case.

Because WooCommerce has done all this work for us, it means we can
access our new field value using this line of code

​get_post_meta( $order->get_id(), ​'_billing_mobile_phone'​, ​true​ )

which uses the WordPress “get_post_meta” function to retrieve the value that
WooCommerce has saved for us.

We then ​echo ​out the value along with an appropriate label, once our code is
fired as part of “woocommerce_admin_order_data_after_billing_address”
action then our new value is displayed, as below.

172
How to change a Shipping
Address Field on the Checkout
Screen
The Problem
In all the examples we’ve looked at so far we have only changed fields in the
billing address, but as a default, WooCommerce allows users to enter a billing
and a shipping address field.

Shipping address fields can be altered in a very similar way to billing address
fields, and we’ll take a look at how to do it in this chapter.

The Solution
Let’s imagine we want to change the shipping company field in the following
ways

● Change it’s label and placeholder to “Company to ship to”


● Make it mandatory (not optional)

Here’s what the shipping company field looks like before we make our
changes

173
To make the change we’ll use a very similar method to when we were
changing billing address fields, but instead of using the
“woocommerce_billing_fields” filter we’ll use the
“woocommerce_shipping_fields” filter.

Here’s our code

function​ ​hwn_change_company_label_shipping​($fields) {
$fields[​'shipping_company'​][​'label'​] = ​'Company to ship to'​;
$fields[​'shipping_company'​][​'placeholder'​] = ​'Company to ship
to'​;
$fields[​'shipping_company'​][​'required'​] = ​true​;
​return​ $fields;
}
add_filter(​'woocommerce_shipping_fields'​,​'hwn_change_company_label
_shipping'​);

If you’ve read any of the previous chapters on adjusting billing fields then
you’ll be familiar with the code above. But we’ll go over it briefly anyway.

We take the ​$fields ​array​ ​that is passed into the filter and then update the
values for the “label”, “placeholder” and “required” elements.

We then return the amended array which will pass our new values back to the
calling code.

174
Once we’ve added our code then the company shipping address field will be
updated to meet our requirements, as you can see in the screenshot below

175
How to change an Address Field
in both the Shipping and Billing
Address on the Checkout Screen
The Problem
In the last few chapters we have looked at how we can change fields in the
billing and shipping address sections on the checkout screen and we have
used the “woocommerce_billing_fields” and “woocommerce_shipping_fields”
filters to make our changes.

But what if we want to update a field in both addresses? In this chapter we will
take a look at how to do that.

The Solution
The solution to this problem is to use the
“woocommerce_default_address_fields” filter that impacts both the billing and
shipping addresses.

As an example of this we’ll make the following change to the company field in
both the billing and shipping addresses

● Change it’s label and placeholder to “Your Company”


● Make it mandatory (not optional)

Here’s the code

function​ ​hwn_update_company_for_shipping_and_billing​( $fields ) {

176
$fields[​'company'​][​'label'​] = ​'Your company'​;
$fields[​'company'​][​'placeholder'​] = ​'Your company'​;
$fields[​'company'​][​'required'​] = ​true​;
​return​ $fields;
}
add_filter(​'woocommerce_default_address_fields'​,
'​hwn_update_company_for_shipping_and_billing​'​, 20, 1);

In the code we use the “woocommerce_default_address_fields” filter in a


similar way to the way we have used the “woocommerce_billing_fields” and
“woocommerce_shipping_fields” filters in previous chapters.

In this example we update the “company” element in the array with new
values for the “label”, “placeholder” and “required” elements, we then return
the updated array so it can be used by the code that has called the filter.

Once we have added the snippet above then the company field is updated in
the billing address field on the checkout form

and the “Shipping Address” section

177
Overriding Default Fields
In this (and the previous) chapter, we’ve always used the “Company Name”
field in all our examples, you may find that if you use the code in those
chapters to change a different field then the code does not have the desired
impact.

Here’s an example using the “woocommerce_billing_fields” filter to attempt to


change the “Street address” field in the billing address. ​Please note this
code will not work.

function​ ​hwn_change_street_address_field​( $fields ) {


$fields[​'billing_address_1'​][​'label'​] = ​'Address line 1'​;
​return​ $fields;
}
add_filter( ​'woocommerce_billing_fields'​ ,
'hwn_change_street_address_field'​ );

178
If we add this snippet then the “Street address” field will not change, this is
because WooCommerce runs some Javascript code on the checkout screen
that updates certain address fields with their international text values.

You would see similar behaviour if you attempted to change any of the
following fields -

● billing_address_1
● billing_city
● billing_state
● Billing_postcode

We can get around this behaviour though, by using the


“woocommerce_default_address_fields” filter that we used earlier in this
chapter. So if we change our code to this

function​ ​hwn_change_street_address_field​( $fields ) {


$fields[​'address_1'​][​'label'​] = ​'Address line 1'​;
​return​ $fields;
}
add_filter( ​'woocommerce_default_address_fields'​ ,
'hwn_change_street_address_field'​ );

Then we will get our desired change

179
This works because the Woocommerce code base gives changes made in the
“woocommerce_default_address_fields” filter a higher priority than changes
made in the “woocommerce_billing_fields” filter, so it will not override our
changes in the same way as before.

The only downside to using this method is that it will change the “Street
address” field in both the “billing “ and the “shipping addresses”.

I think in most cases you would want to change both fields anyway, but this is
something to bear in mind when using this approach.

180
How to Remove the Option for
Users to Supply a Shipping
Address
The Problem
On the default Woocommerce checkout screen an option is provided to supply
alternate shipping details

But what if we wanted to remove this option and force customers to have
products shipped to their billing address?

In this chapter we’ll take a look at how to remove the option from the checkout
screen.

The Solution
The easiest way to solve this is to make a change to the Woocommerce
settings in the Wordpress admin.

If you go to WooCommerce -> Settings and then choose the “Shipping” tab
and the “Shipping options” option

181
You should see a selection of radio buttons that look like this

By default, the “Default to customer billing address” option is selected, but if


you change this to the “Force shipping to the customer billing address” option
and save your changes then the “Ship to a different address?” option should
be removed from the checkout screen.

In case you're wondering, selecting the “Default to customer shipping address”


option will retain the “Ship to a different address?” option and the billing
address fields on the checkout screen. But it will autocheck the “Ship to a
different address?” checkbox

The user is therefore still allowed to not supply a separate shipping address if
they’d prefer, they just need to uncheck the checkbox.

182
Solving the Problem in Code
The setting is stored in a Wordpress option named
“woocommerce_ship_to_destination” which can be one of the following three
values

● shipping (corresponds to the “Default to customer shipping address”


option)
● billing (corresponds to the “Default to customer billing address” option)
● billing_only (corresponds to the “Force shipping to the customer billing
address” option)

As the value is stored as an option value we can overwrite it in code using the
“option_{name of option}” filter.

Using this filter we substitute {name of option} for the name of the option we
want to alter the value of, and then hook a function that returns the correct
value.

Let’s take a look at the code

function​ ​always_return_billing_only​ () {
​return​ ​"billing_only"​;
}
add_filter(
'option_woocommerce_ship_to_destination'​,​'always_return_billing_on
ly'​);

As you can see from the code above we have created a function named
“always_return_billing_only” and hooked it up to the
“option_woocommerce_ship_to_destination” filter which fits the pattern we
described earlier.

The “always_return_billing_only” function only returns the "billing_only" string


meaning that the “Force shipping to the customer billing address” option

183
address will always be used regardless of what is chosen in the admin
interface.

It should be noted that this isn’t an ideal solution, and the more intuitive option
would be to use the admin interface but I wanted to show you how it could be
done this way just in case you ever need to do something similar.

184
How to Add a New Field to the
Checkout Screen
The Problem
In the “How to Add a Field to the Billing Address on the Checkout Screen”
chapter we looked at how to add an extra field to one the address fields on the
checkout screen.

When we add an address field we are able to leverage a number of filters that
Woocommerce provides in order to add an address field, but what if we
wanted to add a field that is not part of an address?

In this chapter we’ll take a look at how to add a “packaging instructions” field
to the end of the checkout form.

The Solution
We’ll begin this section with a look at how we want the new field to look,
here’s a preview of what we’ll see once the field is in place

185
The first step we need to take is to add the HTML mark-up to the checkout
form. To do this we’ll hook into an action named
“woocommerce_after_order_notes” that fires after the order notes have been
added to the checkout screen. We can then echo out some HTML that will
output our packing instructions field to the screen. Here’s our code

function​ ​hwn_add_packing_instructions_field​( $checkout ) {

​ cho​ ​'<div id="packing_instructions"><h2>'​ . __(​'Packing


e
Instructions'​) . ​'</h2>'​;

woocommerce_form_field( ​'packing_instructions'​, ​array​(


​'type'​ => ​'text'​,
​'class'​ => ​array​(​'form-row-wide'​),
​'label'​ => __(​'Packing Instructions'​),
​'placeholder'​ => __(​'Packing Instructions'​),
), $checkout->get_value( ​'packing_instructions'​ ));

​echo​ ​'</div>'​;

}
add_action( ​'woocommerce_after_order_notes'​,
'hwn_add_packing_instructions_field'​ );

As you can see we are hooking a function named


“hwn_add_packing_instructions_field” to the
“woocommerce_after_order_notes” action.

The function begins by ​echo​ing the header for our new field out to the screen.

We then use the ​woocommerce_form_field h ​ elper function to output the


packing instructions field, this function takes three arguments

● The name of the field we are going to add


● An associative array that defines the field we want to add, we pass the
value “text” to the “type” element of the array to ensure we get a text

186
field. We then supply the label and placeholder for the field as we have
done in previous chapters. We also pass a CSS class to the field that
can be used for styling purposes. In this example we just pass
“form-row-wide” which is a class Woocommerce uses to style form
fields. We could supply additional classes if required though.
● Finally we pass a value for the field. For this we use the Woocommerce
helper function ​$checkout->get_value​. This function searches the POST
value collection and the current user meta data to see if it can find a
value that matches the key we supply, which in the example above is
“packing_instructions”. As the code will be unable to find any values the
first time we enter the checkout screen it will leave the value of the field
blank.

Once we add the snippet above the checkout field will show as pictured in the
screenshot at the start of this section.

So, we now have added a package instructions field to the checkout screen,
but the checkout process will ignore any values we input into it. Let’s take a
look at how we can validate and save the field.

Validating the Field


To validate the field we can hook into the “woocommerce_checkout_process”
action, as we can see in the code below

function​ ​hwn_validate_packing_instructions​() {
​if​ ( ! $_POST[​'packing_instructions'​] )
wc_add_notice( __( ​'Please enter some packing instructions.'
), ​'error'​ );
}
add_action(​'woocommerce_checkout_process'​,
'hwn_validate_packing_instructions'​);

187
In the code above we create a function named
“hwn_validate_packing_instructions” and hook into the
“woocommerce_checkout_process” action.

We then check if the “packing_instructions” POST field exists, and if it doesn’t


we output an error notice to the user that should look similar to the screenshot
below

Please note that the name of the POST field we check for should match the
value we passed to the first argument of the ​woocommerce_form_field ​helper
function when we added the field to the form.

If you wanted to add different validation logic then you could change the code
in the ​if s​ tatement to suit your requirements, as an example you could check
to see that the value did not exceed a certain length.

If you do not want to validate the field, then just omit the code above and
Woocommerce will allow the field through regardless of its value and will
accept the field if it’s blank.

Saving the Field Value


To save the value of our new field we can hook into the
“woocommerce_checkout_update_order_meta” action, here’s the code we will
use

188
function​ ​hwn_save_packing_instructions_value​( $order_id ) {
​if​ ( !$_POST[​'packing_instructions'​]) {
update_post_meta( $order_id, ​'packing_instructions'​,
sanitize_text_field( $_POST[​'packing_instructions'​] ) );
}
}
add_action( ​'woocommerce_checkout_update_order_meta'​,
'hwn_save_packing_instructions_value'​ );

The code above creates a function named


“hwn_save_packing_instructions_value” and hooks it into the
“woocommerce_checkout_update_order_meta” action.

Our new function firstly checks if the “packing_instructions” POST value


exists, as in our validation code, the value we check for should match the first
argument we passed to the ​woocommerce_form_field​ function when we
added the field.

If we do find a value for the “packing_instructions” POST value then we use


the Wordpress “update_post_meta” function to save the value against the
order.

The “woocommerce_checkout_update_order_meta” action passes an order


id to all it’s hooked functions so we use this id to save the package
instructions value against the correct order.

We also use the built in Wordpress function ​sanitize_text_field​ to ensure we


are writing a safe value to the database.

Now we have the value of the packing instructions field saved away, we are
ready to display it in the admin order screen.

189
Showing the Field Value in the Admin Order
Screen
In the last section we saved the packing instructions field in the order meta
data so we now just need to retrieve the value from the meta data in order to
display it on the admin order screen.

Let’s take a look at the code we’ll use

function
hwn_add_packing_instructions_value_to_admin_screen​($order){
​echo​ ​'<p><strong>'​.__(​'Packing Instructions'​).​':</strong> '​ .
get_post_meta( $order->id, ​'packing_instructions'​, ​true​ ) .
'</p>'​;
}
add_action( ​'woocommerce_admin_order_data_after_billing_address'​,
'hwn_add_packing_instructions_value_to_admin_screen'​, 10, 1 );

In this code we create a function named


“hwn_add_packing_instructions_value_to_admin_screen” and hook into the
“woocommerce_admin_order_data_after_billing_address” action that fires
after the billing address files have been displayed on the admin order screen.

In the function we ​echo​ out a label for our packing instructions field and then
use the built in WordPress function ​get_post_meta ​to retrieve the meta value
that we saved in the previous section and display it out to the screen.

Our packing instructions value should now show on the order admin screen as
below

190
One final thing to note before we end this chapter is that the code above will
output a “Packing Instructions” label even if the user didn’t supply a value. As
can be seen in the screenshot below

If we wanted to make things easier for the admin user and only show the
packing instructions on the admin screen if the user had supplied a value we
could add some ​if​ logic

function
hwn_add_packing_instructions_value_to_admin_screen​($order){
$packing_instructions = get_post_meta( $order->id,
'packing_instructions'​, ​true​ );
​if​ (!empty($packing_instructions)) {
​echo​ ​'<p><strong>'​.__(​'Packing Instructions'​).​':</strong> '​ .
$packing_instructions . ​'</p>'​;
}
}
add_action( ​'woocommerce_admin_order_data_after_billing_address'​,
'hwn_add_packing_instructions_value_to_admin_screen'​, 10, 1 );

191
Here we save the post meta value into a variable named
$packing_instructions, w​ e​ t​ hen only output the package instructions to the
screen if the ​$packing_instructions v​ alue contains a non empty value.

192
Order Confirmation, My
Account and Users
So far, we have looked at how to get yourself set up as a WooCommerce
developer and then looked at the front-end shop and checkout pages.

In this section we’ll take a look at functionality that supports the customer after
they have made a purchase by looking at the order confirmation and “My
Account” functionality.

   

193
How to Add a Custom Field on
the My Account - Account Details
Page
The Problem
Once users have set-up an account in your WooCommerce store they will get
access to a “My account” section that contains an “Account details”
sub-section.

Users are able to edit personal information in this section, as you can see
from the screenshot below

But what if we want to add a new field to this screen and save the details that
a user enters, in this chapter we’ll take a look at how to do that as we look at
an example that adds a “Delivery safe place” field to the “Account Details”
form.

194
The Solution
We can add a new field to the “Account Details” screen by hooking into the
“woocommerce_edit_account_form”

add_action( ​'woocommerce_edit_account_form'​,
'hwn_add_delivery_safe_place_to_edit_account_form'​ );
function​ ​hwn_add_delivery_safe_place_to_edit_account_form​() {
$user = wp_get_current_user();
​?>
<p ​class​="​woocommerce​-​form​-​row​ ​woocommerce​-​form​-​row​--​wide
form​-​row​ ​form​-​row​-​wide​">
<​label​ ​for​="​delivery_safe_place​"><?​php​ ​_e​( '​Delivery​ ​Safe
Place​', '​woocommerce​' ); ?></​label​>
<​input​ ​type​="​text​" ​class​="​woocommerce​-​Input
woocommerce​-​Input​--​text​ ​input​-​text​" ​name​="​delivery_safe_place​"
id​="​delivery_safe_place​" ​value​="<?​php​ ​echo​ ​esc_attr​(
$​user​->​delivery_safe_place​ ); ?>" />
</​p​>
<?​php
}

As you can see from the code above we create a function named
“hwn_add_delivery_safe_place_to_edit_account_form” and then hook it into
the “woocommerce_edit_account_form” action.

The main part of the function outputs a HTML text field with a label reading
“Delivery Safe Place”.

In addition to this the function also retrieves the current user using the built in
wp_get_current_user()​ Wordpress function that returns an object containing
details about the currently logged in user.

195
We then call the ​$user->delivery_safe_place ​property on the user object to
retrieve any previous value that the user has entered for the “Delivery Safe
Place” field. We will discuss this call further when we look at the code that
saves the value the user has entered.

Once we have added the code above our new field should show on the
“Account Details” screen as shown below

Let’s take look at the code to save the value the user enters

add_action( ​'woocommerce_save_account_details'​,
'hwn_save_delivery_safe_place_to_account_details'​, 12, 1 );
function​ ​hwn_save_delivery_safe_place_to_account_details​( $user_id
) {
​if​( ​isset​( $_POST[​'delivery_safe_place'​] ) )
update_user_meta( $user_id, ​'delivery_safe_place'​,
sanitize_text_field( $_POST[​'delivery_safe_place'​] ) );
}

In this code we hook a function named


“hwn_save_delivery_safe_place_to_account_details” to the
“woocommerce_save_account_details” action, the
“woocommerce_save_account_details” action fires as the values from the
“Account Details” screen are being saved.

196
Note that we supply a priority of 12 when we add the action and specify that
we will only use a single argument in our hooked function.

In the main body of our function we firstly check that the ​delivery_safe_place
POST variable is set, if it is, then we save the value entered into the field to
the user meta data using the WordPress ​update_user_meta f​ unction.

The ​delivery_safe_place ​key that we search for in the POST data corresponds
to the ​id​ and ​name​ field that we supplied when we added the text field in the
“hwn_add_delivery_safe_place_to_edit_account_form” function.

We save the data with a key of “delivery_safe_place”, the way that the
WordPress user code works means that once we have saved this data we are
able to retrieve it from a user object using code like this

$user = wp_get_current_user();
$delivery_safe_place = $user->delivery_safe_place;

Now the data has been saved against the user object it will show every time
the user visits the “Account Details” page and they will be able to change their
data if they wish to supply a new safe place.

197
How to Add an Item to the Menu
in the My Account Page
The Problem
Woocommerce has a “My Account” section that allows the user to access a
number of different pages containing useful account information

As most stores have their own unique selling points, store owners will often
want to bespoke functionality adding to the “My Account” section. In this
chapter we’ll take a look at how to add new menu items to the “My Account”
menu and how can we link these new items to URLs and custom content.

198
The Solution
We can add an item to the “My Account” menu with the following code

function​ ​hwn_add_my_details_link​( $menu_links ){


$menu_links[​'homepage'​] = ​'Home Page'​;
​return​ $menu_links;
}
add_filter ( ​'woocommerce_account_menu_items'​,
'hwn_add_my_details_link'​ );

The code adds a function named “hwn_add_my_details_link” to the


“woocommerce_account_menu_items” filter.

The function takes the array of menu items passed in by the filter and adds a
new item to the array that has a key of “homepage” and a value of “Home
Page”.

Once the code above has been added the new menu item appears on the
menu.

199
Linking the New Item to a Url
We have now added our new item to the menu, but as it stands, the menu
item does not do anything.

The easiest way to add functionality to a menu item is to link it to a URL,


here’s the code that will do this

function​ ​hwn_hook_option_to_url​( $url, $endpoint){


​if​( $endpoint === ​'homepage'​ ) {
$url = site_url();
}
​return​ $url;
}
add_filter( ​'woocommerce_get_endpoint_url'​,
'hwn_hook_option_to_url'​, 10, 2 );

200
The code above adds a function named “hwn_hook_option_to_url” to the
“woocommerce_get_endpoint_url” filter.

The filter passes two arguments to our function

● $endpoint ​which is the value of the endpoint on the URL that has been
clicked
● $url which is the URL that the calling code is going to redirect to, we can
change this value in the function if required

In our function we check the value of the ​$endpoint v​ ariable, if it is set to


“homepage” (which is the key that we added to the URL in our
“hwn_add_my_details_link” function) then we set the value of the ​$url ​variable
to the root URL of our site by assigning it the value of the ​site_url()​ method.

Once the code above is in place, clicking on the “Home Page” menu item will
redirect the user to the root page of the store’s website.

Linking the New Item to Custom Content


Whilst linking a menu item to a URL is a good solution in some circumstances,
we will often want to link a menu item to bespoke functionality that appears
alongside the “My Account” menu rather than redirecting the user to a
completely different page on the website.

To demonstrate how to do this we will firstly add another new item to the
menu with the text “No. of orders”

function​ ​hwn_add_number_of_orders_link​( $menu_links ){


$menu_links[​'ordercount'​] = ​'No. of Orders'​;
​return​ $menu_links;
}
add_filter ( ​'woocommerce_account_menu_items'​,
'hwn_add_number_of_orders_link'​, 40 );

201
As you can see from the code above we create the menu item using the same
filter as we did in the previous section, the only changes that we make are to
pass different values as the key and value of the added array element.

Once our new menu item is in place we need to add something that links our
new menu item to a piece of bespoke functionality.

In this case, I want to link the menu item to a page that displays how many
orders the user has made in the store.

The first thing we need to do to accomplish this is to add some code that links
our menu item to the functionality that will display the user’s order count
information

function​ ​hwn_add_ordercount_endpoint​() {
add_rewrite_endpoint( ​'ordercount'​, EP_PAGES );

}
add_action( ​'init'​, ​'hwn_add_ordercount_endpoint'​ );

The first thing we need to do is register the endpoint that we have added, as
you may remember from the previous section, the endpoint is the value we
specify for the key of the new menu item array element.

So in this case we need to register an endpoint with the value “ordercount”,


we do this by calling the ​add_rewrite_endpoint ​function, as the endpoint
registration needs to occur at the start of the Wordpress loading process we
create a function named “hwn_add_ordercount_enpoint” and register it to fire
on the Wordpress “init” action.

Once we have registered our endpoint using the ​add_rewrite_endpoint


function, WooCommerce automatically adds an action for our endpoint that
follows this pattern “woocommerce_account_{ENDPOINT NAME}_endpoint”,
we just need to replace the “{ENDPOINT NAME}” part of the pattern with the

202
name of our endpoint. So in this example the action hook we need to hook
into is “woocommerce_account_ordercount_endpoint”.

Here’s the code we would use to hook up to the endpoint

add_action( ​'woocommerce_account_ordercount_endpoint'​,
'hwn_ordercount_endpoint_content'​ );

Now let’s take a look at the “hwn_ordercount_endpoint_content” function that


we are hooking into the “woocommerce_account_ordercount_endpoint”
action.

function​ ​hwn_ordercount_endpoint_content​() {
$customer = ​new​ WC_Customer( get_current_user_id() );

$customer_orders = get_posts( ​array​(


​'numberposts'​ => -1,
​'meta_key'​ => ​'_customer_user'​,
​'meta_value'​ => get_current_user_id(),
​'post_type'​ => wc_get_order_types(),
​'post_status'​ => array_keys( wc_get_order_statuses() ),
) );

$number_of_orders = count( $customer_orders );

​if​ ($number_of_orders > 1) {


$notice_text = sprintf(​'Hey %1$s &#x1f600; You\'ve placed
%2$s orders with us - thanks for your custom!'​,
$customer->display_name, $number_of_orders);
}
​else​ {
$notice_text = sprintf(​'Hey %1$s! You haven\'t placed any
orders with us yet, just let us know if we can do anything to help
with your first order &#x1f600;'​, $customer->display_name,
$number_of_orders);
}

203
​echo​ $notice_text;

As we have registered our endpoint and hooked into the appropriate action
created by Woocommerce, we can use the
“hwn_ordercount_endpoint_content” function to render content to the screen.

In the “hwn_ordercount_endpoint_content” function we firstly create an


instance of the ​WC_Customer c​ lass using the Wordpress
get_current_user_id() ​function that returns the id of the currently logged in
user​.

We then use the Wordpress ​get_posts​ function to count how many orders the
current user has made.

Once we have the user information we use it and the information contained in
our ​$customer​ object to output a message to the screen via the PHP ​sprintf
method.

As you can see from the code, we output a different message depending on
whether the current user has placed any orders.

You can see an example message below

204
Making sure Wordpress Can See Our New Endpoint

You may find that after adding the code to add the new “No. of Orders” menu
item and then clicking on the new item you see an error similar to the one
below

205
This is because Wordpress cannot see the new endpoint we added

In order to fix this we need to resave the Permalink setting in Wordpress, this
causes Wordpress to refresh it’s internal list of endpoints and recognize our
new end point.

We can do this by going into the Wordpress admin then going to “Settings” ->
“Permalinks” and clicking the “Save Changes” button.

Checklist

The process of adding a new “My Account” menu item with custom content is
quite involved, so just to recap, here are the steps you need to follow

1. Create a function that hooks into the


“woocommerce_account_menu_items” filter to add a new menu item.
The new menu item should be an array element that has a key with the

206
value of the endpoint to be added and a value that corresponds to the
text you wish to be shown on the menu.
2. Create a function that hooks into the Wordpress “init” action that sets up
a new endpoint using the ​add_rewrite_endpoint​ function.
3. Hook into the new action created by Woocommerce based on the
endpoint we created in the previous step, the action name should follow
this pattern “woocommerce_account_{end point name created in step 1
}_endpoint”.
4. Re save the permalink settings in the Wordpress admin

If you follow the process above each time you add a “My Account” menu item
linked to custom content then you should be successful every time.

Adding the New Item in a Specific Position in


the Menu
In all the examples so far we have inserted the new menu item at the bottom
of the “My Account” menu, but what if we wanted to insert a new item
somewhere in the middle of the existing items?

In order to do this, we would use the PHP ​array_slice m​ ethod and the “+”
​ rray into two seperate pieces and then join it
operator to slice the ​$menulinks a
back together along with our new menu item.

PHP also provides an ​array_splice​ method which allows you to add a new
element at a specific point in an array, but as that method can only deal with
arrays with numerical keys we can’t use it here.

function​ ​hwn_add_number_of_orders_link_in_middle_of_menu​(
$menu_links ){
$menu_item = ​array​(​'ordercount'​ => ​'No. of Orders'​);
$menu_links = array_slice($menu_links, 0, 3, ​true​) +
$menu_item +
array_slice($menu_links, 3, ​NULL​, ​true​);

207
​return​ $menu_links;
}
add_filter ( ​'woocommerce_account_menu_items'​,
'hwn_add_number_of_orders_link_in_middle_of_menu'​, 40 );

In the example above, we do the following

● Create an array named ​$menu_item ​that contains the data we want to


add to the menu
● We then use the ​array_slice m ​ ethod, we pass it the following arguments
○ $menu_links ​-​ ​this is the array that we want to chop up
○ 0 - this is where we want the slice to begin, passing zero as the
argument means we start the slice at the beginning of the array
○ 3 - this where we want the slice to end, passing three alongside
the 0 we passed for the previous argument means we take the
first three items of the array
○ True - we pass this to specify that we want both keys and values
from the array to be included in the slice
● We then use the “+” operator to add the the first three elements of the
$menu_links ​array to our ​$menu_item a ​ rray
● Finally we call ​array_slice ​again, but this time we use 3 as the second
argument and NULL as the third. This means we take a cut of the
$menu_links ​array that begins after the third element. Passing NULL as
the third argument means that the function will take all the remaining
elements of the array in the slice
● We then add the result of the second ​array_slice t​ o the result of our first
array_slice​ and our new menu element

Having changed our code to use the ​array_slice f​ unction and the “+” operator
the “No. of Items” menu item is inserted into the menu underneath the first
three items

208
Altering the Icon of the New Menu Item
If you’re following along using the “Storefront” theme you may have noticed
that all the items we have added to the menu so far have had the same icon

This is because by default, WooCommerce assigns the icon above to all menu
items that have not had an icon explicitly assigned to them.

209
WooCommerce adds the icon via a css rule, that you can see below (the
screenshot is taken from Chrome’s dev tools)

The key parts of the CSS rule are the “font-family” attribute that is set to “Font
Awesome 5 Free” and the “content” attribute that is set to “\f15c”. We can see
the icon here on the Font Awesome website

If we want to change the icon of our new menu item then we firstly need to
choose a new icon from the Font Awesome website, this task is complicated

210
slightly by the fact that the Storefront theme uses icons from version 5 of the
Font Awesome icon set and if we do a search on the website it returns icons
from a number of sets, some of which were released after version 5 so they
won’t work for us.

To get around this we’ll choose an icon from version 4.7 of the Font Awesome
icon set, you can brown these icons here

https://fontawesome.com/v4.7.0/icons/

For our “No. of Orders” item we’ll use a bar chart icon

We now need to create a css style rule to display our icon, here’s what we’ll
use

.woocommerce-MyAccount-navigation ​ul
li​.woocommerce-MyAccount-navigation-link--ordercount ​a​::before {
​content​: ​"f080"​;
}

211
The selector we need to use in our rule will follow this pattern

.woocommerce-MyAccount-navigation ul
li.woocommerce-MyAccount-navigation-link--{menu item id} a::before

As you can see from the code above we follow this pattern and use
“ordercount” as the menu id as that was what we defined earlier.

We also set the “content” attribute to a value that corresponds to the


“Unicode” value for our chosen icon on the Font Awesome website.

Having created the rule we need to add it tour site, there are a number of
ways to do this but for this example, I’m going to use “Additional CSS” field
provided in the customize option for the “Storefront” theme

212
Once the CSS code has been added to the theme the icon should show up in
the “My Account” menu

213
214
How to Remove an Item from the
Menu in the My Account Page
The Problem
In the “My Account” section in WooCommerce there is a menu on the left
hand side of the screen

As you can see from the screenshot above, WooCommerce adds a number of
items to the menu by default, and store owners often want to remove items
that are not relevant to their store.

In this chapter we’ll look at how to remove items from this menu.

215
The Solution
As an example, we’ll remove the “Downloads” option from the menu.

Items can be removed from the menu by hooking into the


“woocommerce_account_menu_items” filter, here’s the code we’ll use

function​ ​hwn_remove_downloads_from_my_account_menu​( $items ) {


​unset​($items[​'downloads'​]);
​return​ $items;
}
add_filter( ​'woocommerce_account_menu_items'​,
'hwn_remove_downloads_from_my_account_menu'​ );

In the code above we take the array of menu items that is passed into the
“hwn_remove_downloads_from_my_account_menu” by the
“woocommerce_account_menu_items” filter and remove the “downloads” item
by using the PHP’s ​unset ​function.

We then return the modified array to the code that called the
“woocommerce_account_menu_items” filter.

Once the code above is in place the “Downloads” option should be removed
from the menu as we can see below

216
Removing Other Items from the Menu
We can remove other items from the menu using the same method but we
​ rray when we call the ​unset
need to pass a different key to the ​$items a
function.

Here’s a code snippet that will remove every time from the menu

function​ ​hwn_remove_all_items_from_my_account_menu​( $items ) {


​//Remove Dashboard from menu
​unset​($items[​'dashboard'​]);

​//Remove Orders from menu


​unset​($items[​'orders'​]);

​//Remove Downloads from menu


​unset​($items[​'downloads'​]);

217
​//Remove Addresses from menu
​unset​($items[​'edit-address'​]);

​//Remove Account details from menu


​unset​($items[​'edit-account'​]);

​//Remove Logout from menu


​unset​($items[​'customer-logout'​]);
​return​ $items;
}
add_filter( ​'woocommerce_account_menu_items'​,
'hwn_remove_all_items_from_my_account_menu'​ );

The code above uses a similar method to the first snippet to remove all of the
menu items.

It’s probably fairly unlikely that you’ll need to remove all the items in the menu
but if you do need to remove an item you can use the code above and just
remove the menu items that you want to keep from the functions code.

Removing Items from the Menu Without Using


Code
This book is all about using code to modify WooCommerce, but there is a way
to remove items from the menu without using code snippets, which we’ll take
a quick look at.

218
To use this method go to the admin area of your Wordpress/WooCommerce
site and select the WooCommerce -> Settings option

Then choose the “Advanced” tab

At the bottom of the “Advanced” screen you should see a section with the title
“Account endpoints”

219
You can remove options from the menu by removing the text in the
corresponding text field, so making this change

220
and then saving the changes will mean the “Downloads” option will be
removed from the menu.

   

221
How to Send Custom Emails
Based on Order Details
The Problem
After users have placed an order in Woocommerce they are sent a
confirmation email by default.

Sometimes, store owners will want to send additional emails depending on


what was contained in the users order. As an example, they may wish to send
an extra email with shipping details or follow up on some after care
instructions for a product in the customer’s order.

In this chapter we will look at how to send additional emails based on order
details.

The Solution
Let’s imagine that a store owner has told us they want to send an extra email
to any customers whose order contains a “flat rate” shipping item.

To send the additional email we will hook into the “woocommrce_thankyou”


action that fires after a user’s order has been processed

function​ ​hwn_send_extra_email_for_flat_rate_shipping​( $order_id )


{
​if​ ( ! $order_id ) ​return​;

$order = wc_get_order( $order_id );

$user_name_and_email = $order->billing_first_name . ​' '​ .

222
$order->billing_last_name . ​' <'​ . $order->billing_email . ​'>'​;
$from = ​'From: '​ . WC_Emails::get_from_name() . ​' <'​ .
WC_Emails::get_from_address() . ​'>'​;

​if​ ( $order->has_shipping_method(​"flat_rate"​)) {
$subject = ​'Flat Rate Shipping'​;
$message = ​"We notice that you chose flat rate shipping on
your order,\r\nbecause of the holidays this may take a little
longer so please be patient :)"​;
}

​if​( $subject & $message) {


wp_mail($user_name_and_email, $subject, $message, $from );
}
}
add_action( ​'woocommerce_thankyou'​,
'hwn_send_extra_email_for_flat_rate_shipping'​, 10, 1 );

In the code above we hook a function named


“hwn_send_extra_email_for_flat_rate_shipping” to the
“woocommerce_thankyou” action.

The function begins by checking the value of the ​$order_id v​ ariable passed
into it, if the variable contains no value then the function takes no further
action.

If the ​$order_id​ variable does contain a value then we pass it to the


Woocommerce ​wc_get_order​ function which returns an object containing
details of the order linked to the order id. There are more details about the
wc_get_order​ function in the “How to Access Order Details” chapter.

The code then uses the customer details form the order object to build up a
from address for the email which is stored in the ​$user_name_and_email
variable.

223
We then use properties from the ​WC_Emails ​Woocommerce class to build a
string that contains the store owners details, we will use this as the “from”
address details for our email.

We then call the ​has_shipping_method f​ unction on the order object,


depending on the value of the argument passed to this function it will return
true if the order contains any items that are being shipped using the method
detailed in the value, in this case we pass “flat_rate”. If the order contains no
“flat_rate” shipping items then the function returns false.

If our order does contain a flat rate shipping item then we set values for the
subject and message text of our email. It would be possible to change this ​if
statement to check for many other order related things, there are more details
about getting information about orders in the “How to Access Order Details”
and “How to View All the Items in an Order” chapters.

Finally if the ​$subject​ and ​$message ​variables contain values (which would
only have happened if our order contains a flat rate shipping item) then we
send an email using the Wordpress ​wp_mail f​ unction.

If you add the code above to your site then the email should be sent after any
order that contains a flat rate item has been processed.

224
The Admin Area
Having looked at front end functionality in the preceding sections of this book.
We will spend our final section taking a look at how we can add functionality to
improve the day to day experience of a store's admin users.

225
How to Add a Field to The
Product Edit Screen
The Problem
Sometimes we want to add fields to products, either to show in the customer
facing shop pages or to make modifications to the checkout or order process.

Whilst we can alter these fields in code, it’s nicer to give the shop
administrators the functionality to alter the fields in the shop admin screens.

In this chapter we’ll look at how we can add a field in the admin screen that
controls whether the “gift note” field that we added in the “How to Add a
Custom Field to a Product” chapter shows on the single product page.

Adding the Field to the Admin Page


We can add our field by adding a function into the
“woocommerce_product_options_general_product_data” action.

function​ ​hwn_add_gift_note_checkbox​()
{
​global​ $post;

woocommerce_wp_checkbox(​array​(
​'id'​ => ​'_gift_note'​,
​'value'​ => get_post_meta( $post->ID, ​'_gift_note'​,
true​ ) ? ​'yes'​ : ​'no'​,
​'label'​ => __( ​'Show Gift Note?'​, ​'woocommerce'​ ),
​'wrapper_class'​ => ​'options_group show_if_simple'​,
​'description'​ => __( ​'Allow users to specify a gift note

226
for this product'​, ​'woocommerce'​ ),
));

}
add_action(​'woocommerce_product_options_general_product_data'​,
'hwn_add_gift_note_checkbox'​);

Let’s take a look at what the code is doing

● At the start of the function we declare the ​global $post v​ ariable, so we


have access to the ​$post i​ nformation in our function, in this case the
$post ​variable will give us information about the product the user is
editing
● We then call the ​woocommerce_wp_checkbox t​ his will render a
checkbox on the screen for us, we pass it the following information
○ id - ​The value that will be populated in HTML element’s value
attribute
○ value - ​The value of the HTML element, as we are adding a
checkbox element, the value can only be “yes” or “no” . These
values will determine whether the checkbox is checked or not. For
this value we call the ​get_post_meta f​ unction to retrieve any data
that has previously been set for the “_gift_note” key for this
product, if the ​get_post_meta ​function returns a value then we
check the checkbox if we find no value then we leave the value
unchecked. By doing this we ensure that the admin user can see
the current state of this value on the edit screen.
○ label ​- The label that will appear before the checkbox
○ wrapper_class - ​The CSS class that will be applied to our HTML
element, we’ll look at this field in greater detail in the “How to Only
Show Custom Edit Screen Fields for Certain Product Types”
chapter.
○ description - ​The description that will appear after the checkbox
field

Following the addition of the above code, we should see the a new checkbox
field added to the “General” tab on the product edit screen

227
Saving the Field Value
Although we have added a field, we have not yet added any code to save the
value that the admin user specifies, we can do this by hooking into the
“woocommerce_process_product_meta” action

function​ ​hwn_save_gift_note_field​($post_id)
{
$gift_note_field = $_POST[​'_gift_note'​];
update_post_meta($post_id, ​'_gift_note'​,
esc_attr($gift_note_field));
}
add_action(​'woocommerce_process_product_meta'​,
'hwn_save_gift_note_field'​);

In this code we first search the ​$POST ​variables for a value with a key of
“_gift_note’ notice how this matches the ​id ​value we specified in the
hwn_add_gift_note_checkbox f​ unction.

Once we have the value we write it to the products meta data using the
update_post_meta ​function, doing this means we can retrieve the value
specified by the admin user whenever we have access to the products data.

228
Using the Value in Customer Facing Pages
Now we have the value specified in our admin field saved to the products
metadata, it is a relatively simple task to access the information.

If we go back to the code we added in the “How to Add a Custom Field to a


Product” chapter to show the gift note field in the single product page we
controlled the display of the gift note text field using a product id

if​ ( $product->get_id() !== 21 ) {


​return​;
}

In the code above we check if the product has a certain id and if it hasn’t then
we return from the function meaning that field will not be displayed. We can
now rewrite this logic to use the metadata

$show_gift_note = get_post_meta( $product->get_id(), ​'_gift_note'​,


true​ );
if​ ( !$show_gift_note ) {
​return​;
}

Here we use the ​get_post_meta ​function to retrieve the data we saved in the
admin screen.

Because of the way a checkbox field works, the value returned into the
$show_gift_note v​ ariable by ​get_post_meta ​when we supply the “_gift_note”
field will either be “yes” or a blank string.

We then use this value to determine whether the gift not field is displayed.

229
Final Notes
Adding fields to the product edit screens can give admin users easy access to
turn functionality that we add as WooCommerce developers on and off.

Using the product edit screens to control functionality provides a much nicer
end user experience rather than allowing functionality to only be modified in
code.

230
How to Only Show Custom Edit
Screen Fields for Certain Product
Types
The Problem
In the “How to Add a Field to The Product Edit Screen” chapter we looked at
how to add a custom field to the product edit screen, but what if we want to
only show our custom field on certain product types?

In this chapter we’ll take a look at how to only show our custom field for
specific product types.

Limiting the Display of the Custom Field by


Product Type
When we add a custom field we often only want it to apply to certain product
types, let’s imagine that in this example we only want our new field to show for
“simple” product types.

How could we do this, we might think that we could add some code like this to
our ​hwn_add_gift_note_checkbox ​function that was defined in the “How to
Add a Field to The Product Edit Screen”.

global​ $post;

$product = wc_get_product($post->ID);

if​ (!$product->is_type(​'simple'​)) {

231
​return​;
}

Let’s take a look at the function in full again with the new code added

function​ ​hwn_add_gift_note_checkbox​()
{
​global​ $post;

$product = wc_get_product($post->ID);

​if​ (!$product->is_type(​'simple'​)) {
​return​;
}

woocommerce_wp_checkbox(​array​(
​'id'​ => ​'_gift_note'​,
​'value'​ => get_post_meta( $post->ID, ​'_gift_note'​,
true​ ) ? ​'yes'​ : ​'no'​,
​'label'​ => __( ​'Show Gift Note?'​, ​'woocommerce'​ ),
​'wrapper_class'​ => ​'options_group'​,
​'description'​ => __( ​'Allow users to specify a gift note
for this product'​, ​'woocommerce'​ ),
));

}
add_action(​'woocommerce_product_options_general_product_data'​,
'hwn_add_gift_note_checkbox'​);

Here we use the ​wc_get_product ​function to return a variable that contains the
product details of the product being edited. We do this by getting the post id
from the global post object and then using ​wc_get_product ​function to return
the product details.

232
We then use the ​is_type ​function on the product object to see if the product is
a “simple” product, if it isn’t, then we exit the function before we output the
custom field.

Why Won’t this Approach Work?


The approach above will only show our custom field on simple products, but
we will run into a problem if a user changes the product type on the edit
screen using the product type drop down in the “Product data” section

Specifically we will run into a problem if an admin user completes the following
steps

● Edits a simple product


● Changes the the drop down to a value other than “Simple product”

In this scenario our field will not be hidden as we can see below

233
We are getting this problem because when the user changes the dropdown
WooCommerce reloads the product fields via Javascript rather than reloading
the page, this means that the logic in our “hwn_add_gift_note_checkbox”
function will not be called.

So how do we get round this? Luckily WooCommerce provides us with an


easy way to hook into the logic that hides fields for different products, and we
can hook into it via the “wrapper_class” attribute that we specify in the array
passed to the “woocommerce_wp_checkbox” function.

Here’s what we need to do -

woocommerce_wp_checkbox(​array​(
​'id'​ => ​'_gift_note'​,
​'value'​ => get_post_meta( $post->ID, ​'_gift_note'​,
true​ ) ? ​'yes'​ : ​'no'​,
​'label'​ => __( ​'Show Gift Note?'​, ​'woocommerce'​ ),
​'wrapper_class'​ => ​'options_group show_if_simple'​,
​'description'​ => __( ​'Allow users to specify a gift note
for this product'​, ​'woocommerce'​ ),
));

By adding the “show_if_simple” class to the list of classes we pass as the


value to the “wrapper_class” key of the array then WooCommerce will handle
all of the hide logic for us and we don’t need to add any additional code.

So here’s our final function -

function​ ​hwn_add_gift_note_checkbox​()
{
woocommerce_wp_checkbox(​array​(
​'id'​ => ​'_gift_note'​,
​'value'​ => get_post_meta( $post->ID, ​'_gift_note'​,
true​ ) ? ​'yes'​ : ​'no'​,
​'label'​ => __( ​'Show Gift Note?'​, ​'woocommerce'​ ),

234
​'wrapper_class'​ => ​'options_group show_if_simple'​,
​'description'​ => __( ​'Allow users to specify a gift note
for this product'​, ​'woocommerce'​ ),
));

}
add_action(​'woocommerce_product_options_general_product_data'​,
'hwn_add_gift_note_checkbox'​);

In the example above our custom field will only show for simple products but
we can get it to show for other products by adding the following values to the
“wrapper_class” value

show_if_simple​ - Show field for simple products


show_if_variable​ - Show field for variable products
show_if_grouped​ - Show field for grouped products
show_if_external​ - Show field for external products

Any values you supply should be separated by a space, so the value

"show_if_simple show_if_grouped show_if_external"

would show the field for all products except variable products.

Final Notes
If we want to show custom edit fields for specific product types we can supply
class names in the “wrapper_class” value we pass to the form field helper
function. WooCommerce will then handle the show/hide logic for us when the
user changes the product type.

235
How to Access Order Details
The Problem
When we are developing WooCommerce functionality in the admin area we
will sometimes need to check the details of an order.

To do this we’ll need access to a WooCommerce order object, we can then


call functions on the object to find out information about the order.

If you’re trying to get a product’s details then you’re most likely in one of two
different scenarios and we’ll look at each of those scenarios in this chapter.

Scenario 1 - We have Access to the Order


Object
As with a lot of the examples in this book we’ll often add code by hooking into
an action or filter.

Most of the actions or filters that we hook into when working with orders will
have access to an order object, as an example let’s take a look at some code
that hooks into the “woocommerce_admin_order_data_after_billing_address”
action.

function​ ​hwn_show_order_info_after_billing_address​($order){
​echo​ ​'<p>The order has an id of '​ . $order->get_id() . ​' and
contains '​. $order->get_item_count() . ​' items. It\'s status is '
. $order->get_status() . ​'.</p>'​;
}
add_action( ​'woocommerce_admin_order_data_after_billing_address'​,
'hwn_show_order_info_after_billing_address'​, 10, 1 );

236
Here you can see that we hook a function named
“hwn_show_order_info_after_billing_address” to the
“woocommerce_admin_order_data_after_billing_address” action and we
specify that our function will accept one argument.

Our function then takes an ​$order​ argument and we use this to output some
information about the current order to the screen.

The “woocommerce_admin_order_data_after_billing_address” action is


invoked just after the billing address details are displayed in the admin order
screen, so the information that we output is displayed just after the address
details as can be seen below

Scenario 2 - We have Access to an Order Id


On some occasions we may need to access order information when we only
have an order id.

As an example, in the previous scenario we looked at details for an order with


an id of 105, so let's look at some code that returns the same details using
only an order id

237
$order_id = 105;
$order = wc_get_order( $order_id );
echo​ ​'<p>Order 105 contains '​. $order->get_item_count() . ​' items.
It\'s status is '​ . $order->get_status() . ​'. It was placed by '​ .
$order->get_billing_first_name() . ​' '​ .
$order->get_billing_last_name() . ​'.</p>'​;

As you can see from the code above we use the w ​ c_get_order​ WooCommerce 
function to retrieve order details when we only have an id available. 

The ​wc_get_order ​function takes an order id argument and then returns an


order object that is essentially the same as the object passed to our function
by the “woocommerce_admin_order_data_after_billing_address” action in
scenario 1.

We then use this object to output some details about the order to the screen

One other thing to note about the ​wc_get_order f​ unction is that it will return
false if it is not able to find an order with a matching id. In the example above
the code would fail if the function could not find an order with an id of 105 as it
would not be possible to call the various order functions when the ​$order
variable contains a value of false.

We could guard against this by adding some ​if l​ ogic to our code.

$order_id = 4444;
$order = wc_get_order( $order_id );
if​ ($order) {
​echo​ ​'<p>Order 105 contains '​. $order->get_item_count() . ​'
items. It\'s status is '​ . $order->get_status() . ​'. It was placed
by '​ . $order->get_billing_first_name() . ​' '​ .

238
$order->get_billing_last_name() . ​'.</p>'​;
}
else​ {
​echo​ ​'<p>Order id '​ . $order_id . ​' was not found.</p>'​;
}

Running the code above we now see a different result with no errors

   

239
How to View All the Items in an
Order
The Problem
In our last chapter we looked at how we could access information about an
order.

In this chapter we’ll take a look at how we can view all the products that were
purchased in an order.

The Solution
Before we can view the products that were purchased in an order we first
need access to an order object, as we covered this in the last chapter we
won’t go over it again, and we’ll just dive straight into some code that shows
how to access the products in an order.

foreach​ ( $order->get_items() ​as​ $item_id => $item ) {


$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$product = $item->get_product();
$name = $item->get_name();
$quantity = $item->get_quantity();
}

As you can see from the code above we use the ​get_items() f​ unction to
access the products in an order and then a ​foreach ​to loop over the items,
let’s take a look at some code that would output the contents of an order to the
screen

240
$order_id = 105;
$order = wc_get_order( $order_id );
foreach​ ( $order->get_items() ​as​ $item_id => $item ) {
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$name = $item->get_name();
$quantity = $item->get_quantity();
​echo​ ​"{$quantity} {$name}(s) (id - {$product_id} variation id
{$variation_id}) were included in the order.<br>"​;
}

The code above uses the same methods as our previous code but gives an
example of how we can work with product data from an order.

Note that instead of using the full stop operator to concatenate (or join) strings
we have used the interpolation method where we insert the value of variables
using squiggly brackets. In order for this method to work you must enclose the
string in double quotes. Either of these methods is valid if you are building up
a text value, and which you use is largely a matter of personal preference.

The output from the code above looks like this

Accessing Shipping Items in the Order


You may have noticed that so far we have only listed the products that were
contained in the order, we have not been able to access any shipping line
items. Let’s take a look at some code that lists product and shipping line items
within an order

241
$order_id = 105;
$order = wc_get_order( $order_id );
foreach​ ( $order->get_items( ​array​(​'shipping'​, ​'line_item'​)) ​as
$item_id => $item ) {
​if​ (get_class($item) == ​"WC_Order_Item_Shipping"​) {
$shipping_name = $item->get_method_title();
$shipping_method_total = $item->get_total();
​echo​ ​"{$shipping_name} shipping is included in the order at
a cost of {$shipping_method_total}.<br>"​;
}
​else​ {
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$name = $item->get_name();
$quantity = $item->get_quantity();
​echo​ ​"{$quantity} {$name}(s) (id - {$product_id } variation
id {$variation_id}) were included in the order.<br>"​;
}
}

In the code above we change the way we call ​$order->get_items ​slightly and
pass an array containing the values “shipping” and “line_item”, this will lead to
the ​get_items f​ unction returning shipping and product items.

When we call ​get_items ​without any arguments it only returns product line
items by default, if we wanted to return only shipping items then we could use
the following code

foreach​ ( $order->get_items( ​'shipping'​) ​as​ $item_id => $item ) {


​//loop over shipping items
}
 
In order to loop through both the shipping and product items we have to add an if 
statement to our code as the shipping and product items are different types of PHP 
classes. 
 

242
Because of this we check the class of the ​$item v​ ariable, if it’s a shipping item then 
we call some code that outputs the shipping details to the screen and if it’s a product 
item we use the same code that we used for previous examples. 
 
Once we run our code that shows both product and shipping items the following is 
returned to the screen 
 

   

243
How to Make Address Field
Editable on the Order Admin
Screen
The Problem
In the “How to Add a Field to the Billing Address on the Checkout Screen”
chapter we added a new field to the billing address named “Billing Mobile
Phone” we also added some code to show the new field in the order admin
screen

In the order edit screen it’s possible to edit the customers billing address by
clicking the small pen icon at the top of the billing address

But when we click this icon our new field does not appear in the edit form like
the other fields in the billing address

244
In this chapter we’ll take a look at how to make our new field editable

The Solution
Before we look at the solution let’s review the code we used to add the “Billing
Mobile Phone” field in the first place

function​ ​hwn_add_mobile_phone_billing_field​( $fields ) {


$fields[​'billing_mobile_phone'​] = ​array​(
​'label'​ => __(​'Mobile Phone'​, ​'woocommerce'​),
​'placeholder'​ => _x(​'Mobile Phone'​, ​'placeholder'​,
'woocommerce'​),
​'required'​ => ​false​,
​'class'​ => ​array​(​'form-row-wide'​)
);
​return​ $fields;
}
add_filter( ​'woocommerce_billing_fields'​ ,
'hwn_add_mobile_phone_billing_field'​ );

I’ve added this code so we can cross reference the ids we used when we add
the code to make the field editable.

Let’s now take a look a the code we need to add to make the field editable

function​ ​hwn_add_billing_address_field​($fields) {
$fields[​'mobile_phone'​][​'label'​] = __( ​'Mobile Phone'​,

245
'woocommerce'​ );
​return​ $fields;
}
add_filter(
'woocommerce_admin_billing_fields'​,​'hwn_add_billing_address_field'
);

As you can see from the code above we hook into the
“woocommerce_admin_billing_fields” filter that gives us access to an array of
the billing fields.

Our mobile phone field is already included in the array that is passed to the
“woocommerce_admin_billing_fields” filter (note that we access the array
element using the id “mobile_phone” which is the id we used to initially add
the element but without the “billing_” text) but WooCommerce only makes a
field editable if it has a value set against the “label” field in the associative
array for the field.

So in the “hwn_add_billing_address_field” function that we hook into the


“woocommerce_admin_billing_fields” filter we add an array element with a key
of “label” and a value of “Mobile Phone”.

Once we have added the code above, the field displays as an editable field on
the order admin screen

We can then edit the field and WooCommerce automatically saves the new
value for us

246
As you can see from the image above, once we add a label for the field via the
“woocommerce_admin_billing_fields” filter the field also appears in the billing
address fields when we are not in the edit form.

In the “How to Add a Field to the Billing Address on the Checkout Screen”
chapter we used the
“woocommerce_admin_order_data_after_billing_address” action to add the
Mobile Phone data to the end of our form. Using this method gives you more
control over how the field is displayed but you don’t get the built in edit
functionality that you get with the method we have used in this chapter.

So you should always consider which method best suits your needs when
deciding which one to use.

247
About The Author
Ian Preston is the owner and creator of the Hard Working Nerd blog, he lives
in the UK with his wife and son.

He got his first coding job in 1999 and has been a full-time developer ever
since, he has worked for major clients in the utility, hospitality and leisure
sectors and has worked on multiple front and back end projects.

He also enjoys writing and likes to try and create content that explains things
in an accessible and easy manner.

You contact Ian directly at [email protected]

248

You might also like