Learning WooCommerce Development by Example
Learning WooCommerce Development by Example
By Example
How to add a Code Snippet to WooCommerce 16
Final Thoughts 29
2
Why bother with do_action and add_action couldn’t we just write stuff directly to the
page? 35
A Contrived Example 37
Priority 39
Removing Filters 40
Passing Arguments 40
Final Thoughts 46
FTP 51
Style Changes 53
3
Changes via the functions.php File 54
What are Variable Products and when should you use them? 72
4
Categories and Attributes and Variable Products 74
Local by Flywheel 81
Conclusion 94
Final Thoughts 99
Problem 102
Solution 102
5
Generating Links to Multiple Categories or Tags 103
Conclusion 105
Conclusion 119
6
How to Check a Products Type 130
How to Access the Details of all The Products in a Users Basket 136
7
Some Example Code that Displays Cart Data 140
8
The Solution 157
How to Add a Field to the Billing Address on the Checkout Screen 169
How to View the New Field and it’s Value on The Order Admin Screen 171
9
The Problem 176
How to Remove the Option for Users to Supply a Shipping Address 181
10
The Solution 198
Checklist 206
How to Remove an Item from the Menu in the My Account Page 215
11
Saving the Field Value 228
How to Only Show Custom Edit Screen Fields for Certain Product Types
231
How to Make Address Field Editable on the Order Admin Screen 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.
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?
For each method, we’ll detail how it’s done and then look at its pros and cons.
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.
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.
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.
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.
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.
Pro
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.
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
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.
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.
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
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.
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.
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
*/
?>
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.
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.
27
refresh the plugins section in the WP dashboard, we should see a new
“Must-Use” menu item.
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
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.
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);
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");
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");
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
do_action("display_some_numbers",true,true);
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.
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.
A Contrived Example
Let’s imagine someone has written a theme for film lovers and they have
included this line of code
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.
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
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
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.
$initial_film_lists = array(
"Hipster" => "La Regle du jeu, Portrait of a Lady on Fire",
"Action" => "Point Break, The Rock"
);
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
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
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.
42
function add_hudson_hawk_to_list_of_films( $films) {
return $films . ", Hudson Hawk";
}
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 );
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.
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.
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
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.
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.
● 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”.
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.
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.
Then choose “Send to” -> “Compressed (zipped) folder”, this should create a
zip file with the same name as your folder
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.
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.
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.
<?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
Child themes give us an opportunity to change files without altering the core
files, let’s take a look at how this works.
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
\wp-content\plugins\woocommerce\templates\single-product\meta.php
to
\wp-content\themes\hardworkingnerdstorefront\woocommerce\single-product\
meta.php
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
\wp-content\themes\storefront
\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
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
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' );
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.
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!).
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
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.
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”.
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
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.
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;
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.
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.
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”
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.
The reason why we went on that brief tour of attributes and categories is that
they are instrumental in setting up variable products.
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.
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.
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 –
To set-up our test dev environment we will use the following tools –
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.
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.
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.
85
So let’s move on to setting up the code IDE.
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
86
Click the “create a launch.json file” text.
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.
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
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.
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.
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.
<?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
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.
<?php
/*
Template Name: HWN Template
*/
Now when we visit our page we should see the code we added
98
<?php
/*
Template Name: HWN Template
*/
$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
100
The code for detecting a tag page is very similar
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
● 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
As you can see, the code is almost identical, we just pass a different term or
taxonomy name.
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
Aside from some differences to the output, the only real difference here is that
we pass “product_tag” as the term/taxonomy name.
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.
Conclusion
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.
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.
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');
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;
<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.
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.
function hwn_add_custom_option(){
global $product;
<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');
Just as a side note you may have noticed the logic here
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.
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation',
'hwn_gift_note_add_to_cart_validation', 10, 3 );
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
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
113
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation',
'hwn_gift_note_add_to_cart_validation', 10, 3 );
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.
$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 );
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
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
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 ) {
}
add_action( 'woocommerce_checkout_create_order_line_item',
'hwn_add_gift_note_to_order_line_item', 10, 3 );
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
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.
We’ll also look at some code that applies a programmatically created coupon
to a user’s basket.
121
function generate_coupon($coupon_code) {
$coupon->set_code($coupon_code);
122
//the product categories included in the promotion,
defaults to an empty array
$coupon->set_product_categories(array());
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->set_code($coupon_code);
return $coupon_code;
}
and then just add in any “set_” functions that you need to use.
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() && ! defined( 'DOING_AJAX' ) )
return;
$coupon_code = 'hwn_generated_coupon';
if (!coupon_exists($coupon_code)) {
generate_coupon($coupon_code);
}
$applied_coupons = $cart->get_applied_coupons();
● 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.
127
function coupon_exists($coupon_code) {
global $wpdb;
$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.
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
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.
$product->get_type()
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
or using is_type()
if ($product->is_type("simple")) {
//logic goes here
}
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.
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.
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.
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.
global $product;
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.
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation',
'hwn_gift_note_add_to_cart_validation', 10, 3 );
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
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
137
$cart_item['line_total_tax'];
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
But you can also write the foreach statement like this
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'].
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.
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.
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
143
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
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.
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.
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.
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
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.
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.
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.
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.
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
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.
function hwn_add_gift_note_custom_fees_based_on_gift_note_length(
$cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$total_fee = 0;
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”
function hwn_change_company_label($fields) {
155
$fields['billing_company']['label'] = 'Your company';
return $fields;
}
add_filter('woocommerce_billing_fields','hwn_change_company_label'
);
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.
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”.
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');
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.
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 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.
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');
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');
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.
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.
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.
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.
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 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 );
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
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
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.
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
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 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
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.
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
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
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
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.
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.
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
echo '</div>';
}
add_action( 'woocommerce_after_order_notes',
'hwn_add_packing_instructions_field' );
The function begins by echoing the header for our new field out to the screen.
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.
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.
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.
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' );
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.
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 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'] ) );
}
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
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.
200
The code above adds a function named “hwn_hook_option_to_url” to the
“woocommerce_get_endpoint_url” filter.
● $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
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.
To demonstrate how to do this we will firstly add another new item to the
menu with the text “No. of orders”
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.
202
name of our endpoint. So in this example the action hook we need to hook
into is “woocommerce_account_ordercount_endpoint”.
add_action( 'woocommerce_account_ordercount_endpoint',
'hwn_ordercount_endpoint_content' );
function hwn_ordercount_endpoint_content() {
$customer = new WC_Customer( get_current_user_id() );
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.
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.
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
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.
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 );
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.
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.
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
217
//Remove Addresses from menu
unset($items['edit-address']);
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.
218
To use this method go to the admin area of your Wordpress/WooCommerce
site and select the WooCommerce -> Settings option
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.
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.
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 :)";
}
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.
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.
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.
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');
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.
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
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.
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.
Specifically we will run into a problem if an admin user completes the following
steps
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.
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' ),
));
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
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.
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.
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.
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.
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.
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.
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
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
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.
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.
248