unit-test-being-run

Getting Started with Unit Testing in WordPress – Part 1

While unit testing is a big deal in many development communities (Ruby on Rails for example) it’s not something the typical WordPress developer does and that’s a crying shame.

Even many of the really good WordPress developers often aren’t doing unit testing.

Even worse the few that are assume a huge level of knowledge is had by readers of their content. That is if they even write about it.

The few more beginner oriented resources on unit testing stop at beginner level. They don’t take your from ‘0 to hero’ as it were.

My goal is to teach you the basics of how to unit test your work and walk you through to a fairly advanced level with a library of test patterns to use for various scenarios.

Over the course of this series on Unit Testing we’ll cover

  • Tools of the trade like PHPUnit, WP_Mock, Travis CI
  • Starting to write some tests
  • Building out a decent library of test patterns
  • and more

Let’s get started by looking at what unit testing is.

What is Unit Testing

Unit testing is the practice of writing automated tests that can be run against your functions/methods to verify their output.

This lets us make sure that our code works as we expect it to work. It helps us to verify the quality of our code as we can also send it bad data and make sure that bad data is handled properly according to our tests.

Why Should you be Unit Testing

Let start with a story. We’ll call our protagonist Curtis M to preserve his identity. He had just shipped a huge client site. It had 3 types of roles with lots of custom capabilities.

It had a custom calendar that looked back to custom post types for bookings and custom post types that interfaced with notes that our coaches could take on students.

He shipped it with a bug, which happens. His client reported the bug and he went in and ‘fixed’ it.

Unfortunately it had been 3 weeks since he wrote the code and since it wasn’t fresh in his mind, he broke another part of the system.

Two weeks later the client found that bug and came back with a request to fix it.

So Curtis M fixed it and guess what…yeah there was another bug.

Eventually Curtis M did close up the project but his extra bug fixing time turned it in to a project without profit so he was grumpy.

Less code breaking

If we skip the technical jargon, we should write tests for our code because it means that we’re less likely to break things when you make changes later. You’ll know that your current changes don’t break old code because your tests still pass.

Then you can have a party.

Simpler Testable code

If you’re planning on unit testing your code you’re going to write code that is easily testable which means you’re likely to end up with smaller abstracted methods or functions.

So no massive 300 line functions combining logic and HTML and more logic. More likely it will be 4 or 5 functions with the logic cleanly separated out and the HTML built in a single function.

Since a function only does one thing you should end up with better function names which will also make your code easier to read. Code that’s easier to read is a good thing not only for the next developer that comes to the project, but for you in 6 months who has totally forgotten exactly what you did on the project.

Save time

Before testing my typical development cycle was something like:

  • Make some edits
  • Go to my browser
  • Add needed data
  • Save data
  • Check for output (WP_DEBUG is on right?)
  • Click around the WordPress admin or the rest of the site once I think I’m done and find where my code broke something else
  • Deal with clients finding problems later that you didn’t think of

Once I started testing it looks like:

  • Make some edits
  • Run phpunit in my terminal
  • Check results
  • Fix issues or move on

The biggest time saver is that later when I write new code I can run those tests and I can tell if something is broken. I don’t have to spend the time clicking around my browser to make sure that things are working properly, my tests tell me.

The net time saved of my tests telling me if something works is HUGE compared to the time it takes you to click around a browser and you’re going to forget to click something.

Tests First or Last?

If your going with a formal Test Driven Development (TDD) methodology you’re actually going to write your tests first. That means you’ll figure out what a particular function is supposed to do and then write the test to make sure it does it properly.

Then you write the actual code needed to pass the test. The code you write is the minimum possible needed to pass the test. That may mean you only test for a boolean value returning true, and all your function does is return true.

Going this method helps you keep your code lean and tight as well. You start out writing a test to check for an array, then as you write the code you realize there are a few other steps needed but you still build your function for that specific array and you break out the other functionality it to separate functions you can also test.

You build out the other pieces you need and test those. Hence it’s likely that you have smaller easily testable functions/methods around.

Testing after would mean you build out your functionality and then write your tests for the functionality and it called Regression Testing. So you write the tests to pass as things work now.

That may mean you write it so that current bugs pass the tests. It will help you find bugs in your code though.

Overall writing tests firsts typically results in better code quality so work towards TDD.

One Final Story

This story is about a completely different developer but we’ll still protect his identity by calling him C McHale.

He wrote a plugin for Restrict Content Pro and did some regression testing on it. He found that a filter he thought was working wasn’t quite working as expected so he fixed it.

Fixing it broke one of his other tests, so he fixed that as well.

No one knew that both broke. He never shipped broken code.

The total turn around for fixing both items was small. Even the tests were written in an hour.

Next time we’ll walk through some of the tools that you will come across as you look at unit testing in WordPress.

Hide WooCommerce reviews

While reviews can be a good thing for many stores, not all clients want reviews on their site.

Built in, WooCommerce has the option to turn off the ‘stars’ in reviews but no way to totall remove the WooCommerce reviews from your site.

Today we’re going to tackle removing reviews totally from your site, leveraging the built-in option to remove review stars in the WooCommerce admin.

I’m using a stock WooCommerce install with the default WooCommerce data which can be imported from the plugin dummy-data folder.

Starting our Plugin

Our first step is to get a plugin base going. We want to make sure that our plugin deactivates itself if WooCommerce is not active and we need to let our user know why the plugin didn’t activate.

Add a folder to your WordPress plugins folder called hide-reviews-for-woocommerce and inside that folder create a file called hide-reviews-for-woocommerce.php.

In to that file paste the code below.

<?php
/*
Plugin Name: Hide Reviews for WooCommerce
Plugin URI: http://sfndesign.ca/
Description: Hides all reviews in WooCommerce if you choose not to use the stars as ratings
Version: 1.0
Author: SFNdesign, Curtis McHale
Author URI: http://sfndesign.ca
License: GPLv2 or later
*/
 
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
 
class Hide_Reviews_For_Woocommerce{
 
    function __construct(){
 
        add_action( 'admin_notices', array( $this, 'check_required_plugins' ) );
 
    } // construct
 
    /**
     * Checks for WooCommerce and GF and kills our plugin if they aren't both active
     *
     * @uses    function_exists     Checks for the function given string
     * @uses    deactivate_plugins  Deactivates plugins given string or array of plugins
     *
     * @action  admin_notices       Provides WordPress admin notices
     *
     * @since   1.0
     * @author  SFNdesign, Curtis McHale
     */
    public function check_required_plugins(){
 
        if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ){ ?>
 
            <div id="message" class="error">
                Hide Reviews for WooCommerce expects WooCommerce to be active. This plugin has been deactivated.
            </div>
 
            <?php
            deactivate_plugins( '/hide-reviews-for-woocommerce/hide-reviews-for-woocommerce.php' );
        } // if woocommerce
 
    } // check_required_plugins
 
} // Hide_Reviews_For_Woocommerce
 
new Hide_Reviews_For_Woocommerce();

Now that we have a base we need to add the functionality to remove the reviews tab from single products.

woocommerce_product_tabs

WooCommerce provides us with a setting to remove the stars in ratings so we’ll leverage that to remove all reviews instead of creating our own setting.

WooCommerce stores this option in the wp_options table and it’s called woocommerce_enable_review_rating.

    function __construct(){
 
        add_filter( 'woocommerce_product_tabs', array( $this, 'kill_wc_reviews_tabs' ), 98 );
 
        add_action( 'admin_notices', array( $this, 'check_required_plugins' ) );
 
    } // construct
 
    /**
     * Removes the WooCommerce Reviews
     *
     * @since 1.0
     * @author SFNdesign, Curtis McHale
     *
     * @param array         $tabs           required            The tabs array
     * @return array        $tabs                               Our modified tabs array
     */
    function kill_wc_reviews_tab( $tabs ){
 
        $review_setting = get_option( 'woocommerce_enable_review_rating' );
 
        if ( 'no' === $review_setting ){
            unset( $tabs['reviews'] );
        }
 
        return $tabs;
 
    } // kill_wc_reviews_tab

Looking at the above code you can see we added a new filter call to woocommerce_product_tabs and we call our kill_wc_reviews_tab function.

Then we get the value of woocommerce_enable_review_rating and if it’s set to no we use the PHP function unset to remove the reviews from the array of tabs that should show up for a product.

What about the widgets

It’s all ‘great’ that we removed the ratings tab, but did you know that you’re still going to see 2 ratings widgets? If you had some data in reviews already they’re even still going to show the ratings.

They actually even show the stars, even though we have told WooCommerce not to shows stars with ratings.

Here is our code to remove the 2 widgets from the WordPress Admin.

    function __construct(){
 
        add_action( 'widgets_init', array( $this, 'kill_wc_ratings_widgets' ), 11 );
        add_filter( 'woocommerce_product_tabs', array( $this, 'kill_wc_reviews_tabs' ), 98 );
 
        add_action( 'admin_notices', array( $this, 'check_required_plugins' ) );
 
    } // construct
 
    /**
     * Removes WC widgets we do not want
     *
     * @since 1.0
     * @author SFNdesign, Curtis McHale
     *
     * @uses unregister_widget()            Unregisters the named widget
     */
    public function kill_wc_ratings_widgets(){
        $review_setting = get_option( 'woocommerce_enable_review_rating' );
 
        if ( 'no' === $review_setting ){
            unregister_widget( 'WC_Widget_Top_Rated_Products' );
            unregister_widget( 'WC_Widget_Recent_Reviews' );
        }
 
    } // kill_wc_ratings_widgets

We start by using the widgets_init action and call our kill_wc_ratings_widgets function.

From there we get the woocommerce_enable_review_rating option and if it equals no we unregister the WooCommerce widgets with unregister_widget.

That’s it, we now can turn off the reviews totally by toggling the ratings stars checkbox.

You can download the plugin for free from it’s Github Repository. Support and automatic upgrades are only provided via license purchase at sfndesign.ca

Screencast

The State of User Facing Documentation in WordPress Products

Last time I talked about why you should be documenting your code.

Today we’re going to talk about why just documenting your code and then posting those generated docs is not the way to document your plugins/themes for your end users or developers that may be using your plugin.

But it’s documentation right…??

No it’s not really documentation, just like adding a social media button to your site is not a ‘social strategy’.

Adding auto-generated docs to your site is a great way of fooling yourself in to thinking you’ve thought about documenting your plugin or theme.

Let my retract that, you did think about it and you decided you actually didn’t care about the documentation and took the easiest way out.

The best way to illustrate this is to look at some examples of documentation in some WordPress projects.

Easy Digital Downloads

An example of decent documentation is the Easy Digital Downloads Docs. Here we have instructions on a number of the EDD plugins, shortcodes, and documentation on the Developer API.

Take a look at the documentation for EDD_Graph. Here we have some examples of how to use the code. Unfortunately not all of the actions/filters are extensively documented, some like edd_payment_delete aren’t really that useful.

Sure it makes sense to an experienced developer but someone that’s just starting to dig deep on their WordPress projects is going to be lost.

WooCommerce

How about we look at WooCommerce Documentation. It’s got a number of areas and does a pretty good job of user facing documentation.

There are still some gaping holes though.

Do you need Composite Products, Chained Products, Group Products, or Product Bundles? Well they have some documentation on them and they do compare them. Go try and find a video on exactly what Composite Products does though.

You’re not going to find it. I’ve talked to numerous WooCommerce developers that are awesome (and have even worked for WooThemes) that have no idea what Composite Products actually does. How is any WordPress or WooCommerce general site owner supposed to have any idea which option they should use?

Now how about the developer documentation. Yup that’s right auto-generated docs.

Sure if we dig around we can get a list of classes but no links to examples on how to use them.

Acutally it tells you to look at the generated documentation for a ‘full’ list. So the list you see, may not be the actual full list of classes available in WooCommerce.

We can also find a hook reference but again none of it actually links back to the code and there are no examples at all without further digging in documentation.

As a developer that regularly works with WooCommerce I simply don’t bother looking at anything but the API docs since the rest of the site simply is barely useful as I build client sites.

SearchWP

SearchWP starts off with a great tutorial video and then covers everything from the philosophy behind the plugin to how to debug issue with the plugin.

Check out the documentation on searchwp_common_words or searchwp_post_statuses. Explanations and examples of code using the filter.

SearchWP is one of the best documented projects in the WordPress space I’ve seen. But really that’s how all our commercial projects should be.

The simple fact is that only doing inline documentation (even exceptional inline documentation with lots of comments) is ‘barely enough’ documentation for your project. Not taking it further means that you’re effectively locking out all but very experienced developers that can read each line of your code.

Sure you may think that’s fine since if you can’t read every line of code you shouldn’t be working on the project. So then no one could start doing work till they were an awesome developer and since most WordPress projcets don’t provide good documentation you have no real introductory way to start learning.

Limit content by product purchase with Easy Restricted Content for WooCommerce

A while ago I released a book called Don’t be an Idiot: Learn to run a viable business and with it I delivered a set of interviews that were only available to purchasers.

Of course I didn’t want people who didn’t purchase to be able to see the videos so I had to write some code to restrict my page.

Now I’ve written a plugin called Easy Restricted Content for WooCommerce (available for $39.99 for a single site) that lets you do the same thing without any code at all.

Lets see how we do it.

Getting Started

I’ve already got WooCommerce installed and a ‘book’ product added. I also have Easy Restricted Content for WooCommerce installed and activated.

Now we need to create a page to hold our custom interviews to go with our product. Go to pages and choose add new. Now name your page ‘Awesome Interviews’ then put some content in the page.

pages-add-new 14-07-15, 8.32.49 AM

Restricting the page to a product purchase

Now on the right side of the page screen you’ll see a box called ‘Restrict Content’. Click on the select box to see the available products in your store. You can type inside the select box and it will start to filter the available products based on what you type.

Find the product you want users to purchase to be able to see your content and click on it to select it.

restrict-typing 14-07-15, 8.46.53 AM

You can also choose to totally remove the page from the WordPress menu for users that haven’t purchased the product. To do this check the box below our select box.

Creating a Custom Message

By default Easy Restricted Content for WooCommerce will provide a message to users that says:

This content is restricted to users that have purchased $product

$product will be the name of the product with a link to the product so that someone could go purchase it.

If you want to provide a custom message, maybe a sample video interview then you can do this via the custom message option in Easy Restricted Content for WooCommerce.

On your page under the main content box is a content area called ‘Custom Restricted Message’. Check the box that says ‘Add a custom message:’ to see the new content entry area open.

Preview

Now type your custom message in to the editor. If you want to link to the product in your content then you can use the built in WooCommerce button in the WordPress editor.

When you’re done make sure you save the post and make sure it’s published for your users.

Making sure that users create an account

Yes our product is restricted, but we’re not quite done yet since by default users can purchase as a ‘guest’ which means they don’t have an account. We need users to have an account because they can only see the content if they are signed in.

Let’s change the default settings of WooCommerce to ensure that our users are required to create an account.

Go to WooCommerce Settings, and choose the Checkout tab. Now you need to uncheck the box that says ‘Enable Guest Checkout’.

require-accounts 14-07-15, 8.40.45 AM

Once you have the guest checkout box unchecked, scroll to the bottom and click save to save the setting.

That’s it, you now have your content restricted by product purchase on your site.

Screencast

Why you SHOULD document all your code

There is a school of thought that says if a function/method/class is small and abstracted you don’t need to write a documentation block for it. So write them small and you can skip the documentation.

Then there’s me who say, document your function/methods/classes and stop being lazy.

Yeah I said lazy and here is why I think you should be documenting ALL THE THINGS.

1. Future You Isn’t as Smart as Current You

During that late night coding session fueled up on coffee you have so many awesome ideas. You write your best code ever complete with unicorn tears.

Unfortunately future you is going to think you’re an idiot when you cut corners and didn’t write that documenation.

Future you will remember that you came up with an awesome solution here, but have no idea how it actually works. Where does one get more unicorn tears so you can understand what’s going on now?

Writing proper documentation on your functions may just let future you in on the late night epiphany you had for your brilliant solution.

2. Future devs are smarter than you

Developers are a pretty arrogant bunch.

We think that whatever our current solution is, it’s better than the code that was written before.

We conviently ignore all those times we look at terrible code then when we dig through source control we realize that bad code is our fault.

Of course we have no idea if scope changed at the last second on the previous developer and the code simply needed to work to hit a deadline.

Maybe the previous developer had a family issue that meant they simply couldn’t focus on the task at hand. I know something like having my kids in the hospital would do that to me.

Writing proper documenation is going to help show a future developer that you’re not an idiot. I’ve put comments in acknowledging that a part of the code feels like a ‘hack’ but we needed to launch so it ‘works’ for now.

Sure that future developer may still dislike the code they see, but they at least don’t think you’re a moron.

3. You’re going to learn

Spending time documenting the parameters that are used with a WordPress filter or action is going to force you to actually learn what is happening.

It’s so easy to copy/paste some code from a tutorial and have a general idea of what’s happening in it. Of course you may have no idea what that variable being passed in to your filter actually is.

Take some time, track the filter down and look at the root function. Figure out what the data being passed is and then write up the proper documentation for it.

You’re going to understand the WordPress internals so much better and you’re on your way to becoming a better WordPress developer.

4. You’re not lazy

When I look at code that has no documentation it’s usually a sign of a lazy developer all around.

Often I’m going to find a bunch of other code that’s been tried and then commented out but never deleted.

So now I have to dig through commented out code in addition to all the other code in the project.

Maybe you clean up after yourself, but I’m on edge as soon as I see that we don’t have documentation on our code.

Code Documenation is not…

So you do actually document your code, but that’s not really user facing or outside developer facing documentation for your project. It’s lazy to just generate documentation and call it your plugin documentation.

It’s like putting social media buttons on your site and then telling yourself that you have a social strategy. You don’t, you have extra javascript running on your site and usually a bunch of 0’s showing nobody liked your articles.

In a future post I’ll cover what proper user facing documentation consists of.

WordPress Documentation stanards

If you don’t know how to document your code then check out the WordPress Documenation Standards.

Each project reference them and follow them. You’ll start understanding more and write better code if you do.

Using pre_get_posts with meta_query

pre_get_posts is an awesome action (does anyone else think of it as a filter like me) that lets you alter the WordPress query arguements just before WordPress gets it’s post.

Say you want to add 20 posts to RSS feeds instead of the standard WordPress 10 posts.

function more_rss_pleeese( $query ){
    if ( is_feed() ){
        $query->set( 'posts_per_page', '20' );
    }
}
add_action( 'pre_get_posts', 'more_rss_pleeese' );

Now all your feeds have 20 posts while your pages still have 10 posts.

It has a few tricks for the first timer though. Did you realize that hooking pre_get_posts will also change your posts queries in the WordPress admin? Make sure you use the is_admin() conditional to exclude the WordPress admin unless you really want to change it.

Everything WP_Query has right??

In theory pre_get_posts can do everything that is available in WP_Query right??

Riiiight??

Yeah actually it can’t quite do everything. Specifically it can’t use the full awesomness avalibale to meta_query in WP_Query.

Lets start by looking at how we’d write our WP_Query to get posts that either don’t have a meta_key set at all, or that have a meta_key that matches the current user.

<?php
$args = array(
    'meta_query' => array(
        'relation' => 'OR',
        // this is the part that gets a key with no value
        array(
            'key'     => '_restricted_key',
            'value    => 'oeusnth', // just has to be something because of a bug in WordPress
            'compare' => 'NOT EXISTS',
        ),
        array(
            'key'     => '_restricted_key',
            'value'   => get_current_user_id(),
            'compare' => '==',
        ),
    ),
);
 
$custom_posts = new WP_Query( $args );
 
// now your loop
?>

Our first array would get any post that doesn’t have our meta_key _restricted_key set at all. WordPress has a ‘bug’ that means you have to set something for the value here. So I just mash the keyboard to get something in there.

Our second array would get any posts with the same meta_key with the value that matches our current_user_id. So if it was 42, we’d get posts with the value 42 for the meta_key.

Now take a look at the relation arguement. That allows us to specify the relationship between the 2 arrays. So it says get all posts with the key not set OR with the key value of 42.

Awesome, now we dance.

Now lets look at that in the context of pre_get_posts.

function custom_posts( $query ){
 
    if ( ! is_admin() ){
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            // this is the part that gets a key with no value
            array(
                'key'     => '_restricted_key',
                'value    => 'oeusnth', // just has to be something because of a bug in WordPress
                'compare' => 'NOT EXISTS',
            ),
            array(
                'key'     => '_restricted_key',
                'value'   => get_current_user_id(),
                'compare' => '==',
            ),
        );
    }
 
}
add_action( 'pre_get_posts', 'custom_posts' );
?>

Theoretically we should now get the same results but are setting the WP_Query args just before it gets the posts from the database.

Unfortunately it won’t work. We can pull the ‘relation’ field and either one of the arrays and find that they work on their own but they don’t work together.

So how on earth do we use pre_get_posts to do a double array style meta_query?

Magic or really just another filter

To accomplish our tasks we need to actually use a filter in WordPress called posts_where.

posts_where allows us to filter the SQL that WordPress is using directly by adding a WHERE claus to our code. So lets take a look at our new pre_get_posts filter which also uses the posts_where filter to get posts with 2 meta_key values.

<?php
function custom_posts( $query ){
 
    if ( ! is_admin() ){
        $query->set( 'meta_query', array(
            // this is the part that gets a key with no value
            array(
                'key'     => '_restricted_key',
                'value    => 'oeusnth', // just has to be something because of a bug in WordPress
                'compare' => 'NOT EXISTS',
            ),
        );
    }
 
    if ( is_user_logged_in() ){
        add_filter( 'posts_where', 'custom_where' );
    }
 
}
add_action( 'pre_get_posts', 'custom_posts' );
 
function custom_where( $where = '' ){
 
    global $wpdb;
 
    $where .= " OR (( $wpdb->postmeta.meta_key = '_restricted_key' AND $wpdb->postmeta.meta_value = get_current_user_id() ))";
 
    remove_filter( 'posts_where', 'custom_where' );
 
    return $where;
 
} // custom_where
?>

Now our above code uses a single array in our pre_get_posts. That array will get posts that do not have a meta_key set.

Then if is_user_logged_in (since if they don’t have a user id we can read we can’t get other posts) we add our new filter to our SQL. This filter says OR if the meta_key _restricted_key has a meta_value of our current user id.

Now we get the results we want.

Yes it’s a bit confusing for a new person with WP_Query but it works.

Screencast

Restrict Content Pro or Paid Memberships Pro

There are a bunch of membership plugins out there. Look at the title, I’m not covering them all today. If you want a comparison of way more membership plugins/products for WordPress then you need to go read these posts from Chris Lema.

Today I’m going to tell you about the 2 membership plugins I use the most in my client work and why/when I choose one over the other.

  1. Paid Memberships Pro
  2. Restrict Content Pro

A note on Code Quality

I’m a developer so of course I’m going to talk about the code quality of a plugin. Both plugins are great as far as I’m concerned. Nothing has ever stuck out as terrible to me.

Yes each plugin has parts I wish were built different but code I wrote 6 months ago also falls in to that category. Every developer writes code that they aren’t proud of later.

Cost

PMPPro is totally FREE. None of the code costs anything. There is a cost for support and the official though. If you’re just getting in to using it then you really should pay for it since the documentation is great.

RCP is not free but it’s not very expensive at $42 USD a year for a single site. For $155 you can get all the plugins that are around for RCP as well so that’s what I recommend clients get out of the gate. Then they can use RCP on any future sites and have all the plugins they may need already. There are also a bunch of free plugins for RCP in the WordPress.org repository.

Depends on Scope 99% of the time

Usually the requirements of the project decide which plugin I’m going to use for me. Let’s look at a few examples.

The first simple project requirement that pushes me to one plugin or the other is if we need to have a WordPress Multisite site tied to a subscription. If we do than I pick PMPPro since there is already a plugin that does that for you.

One currently doesn’t exist for RCP.

If you need to build a job board (and I’ve built a few) then you want to use RCP since it already has a bridge for WP Job Manager.

Do we need memberships and a downloadable store? Then RCP is your choice with it’s member discounts for Easy Digital Downloads.

There are a whole host of other plugins that are only found on one platform so in almost every site I’ve built the choice has already been made for me by the requirements.

If either option works then I usually end up choosing RCP. It just feels ‘easier’ to work with overall, which is possibly just habit/experience with RCP and many of Pippin’s other plugins.

Why use shuffle to randomize WordPress Comments

Last week I showed off a plugin called Random Reviews for WooCommerce. Really it was an assignment given to my students at BCIT and I wanted to provide an example for them.

One of the questions that came up was:

Why did you use shuffle() instead of array_rand()?

Good question and there are lots of bad code examples out there so lets walk through what each function does in the context of my plugin.

First we’ll look at what the functions do.

shuffle()

Acording to the PHP documentation shuffle():

This function shuffles (randomizes the order of the elements in) an array.

It returns true or false so it either did or didn’t work. Lets look at an example.

$foo = array( '1', '2', 'something' );
 
shuffle( $foo );
 
// now $foo could look like or at least something random
array( '2', '1', 'something' );

How about a more complex array example.

$foo = array(
    '0' => array( 'something', 'nothing', 'of' ),
    '1' => array( 'blue', 'green', 'pink' ),
    '2' => array( '1', '2', '3' ),
);
 
shuffle( $foo );
 
// could look like...
array(
    '1' => array( 'blue', 'green', 'pink' ),
    '0' => array( 'something', 'nothing', 'of' ),
    '2' => array( '1', '2', '3' ),
);

It’s worth noting that in earlier versions of PHP shuffle() wasn’t really good at being random but that’s not a problem now. Really it’s not a problem for my random reviews either since as long as the comments are ‘fairly random’ I’m okay.

array_rand()

According to the PHP documentation array_rand():

Picks one or more random entries out of an array, and returns the key (or keys) of the random entries.

So it has 2 parameters

array_rand( $array, $pick )

  • $array – the array you want randomized
  • $pick – how many elements you want out of the array

And a usage example.

$foo = array( '1', '2', 'something' );
 
$random_foo = array_rand( $foo );
 
// now $random_foo could look like or at least something random
array( '2', '1', 'something' );

And now our more complex array.

$foo = array(
    '0' => array( 'something', 'nothing', 'of' ),
    '1' => array( 'blue', 'green', 'pink' ),
    '2' => array( '1', '2', '3' ),
);
 
$random_foo = array_rand( $foo );
 
// $random_foo could look like...
array( '1', '0', '2' );

So while shuffle leaves our array intact and just shuffles things around array_rand only gives us the keys. That means later on we still need to have the $foo variable around and then reference the ‘random’ bits we want by the keys in $random_foo.

That may be a great thing to do sometimes, but not for our random comments. We can see why in a second when we figure out what type of array we’re dealing with and how WordPress handles the comments array.

shuffle to randomize comments

Here is a basic version of our plugin code that could be dropped in to your theme functions.php file to randomize all comments.

function wptt_random_comments( $comments, $post_id ){
    shuffle( $comments );
    return $comments;
} // wptt_random_comments
add_filter( 'comments_array', 'wptt_random_comments', 10, 2 );

Now lets look at what WordPress gives us in our $comments variable passed to our filter.

Array
(
    [0] => stdClass Object
        (
            [comment_ID] => 2
            [comment_post_ID] => 1
            [comment_author] => admin
            [comment_author_email] => admin@wpthemetutorial.com
            [comment_author_url] => 
            [comment_author_IP] => 192.168.50.1
            [comment_date] => 2014-05-30 04:31:50
            [comment_date_gmt] => 2014-05-30 04:31:50
            [comment_content] => I'm an admin and I'm making a comment now.
            [comment_karma] => 0
            [comment_approved] => 1
            [comment_agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4
            [comment_type] => 
            [comment_parent] => 0
            [user_id] => 1
        )

    [1] => stdClass Object
        (
            [comment_ID] => 3
            [comment_post_ID] => 1
            [comment_author] => author-one
            [comment_author_email] => curtis+authorone@curtismchale.ca
            [comment_author_url] => 
            [comment_author_IP] => 192.168.50.1
            [comment_date] => 2014-05-30 04:47:31
            [comment_date_gmt] => 2014-05-30 04:47:31
            [comment_content] => Author one is making a comment and I'm an author.
            [comment_karma] => 0
            [comment_approved] => 1
            [comment_agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4
            [comment_type] => 
            [comment_parent] => 0
            [user_id] => 2
        )

    [2] => stdClass Object
        (
            [comment_ID] => 4
            [comment_post_ID] => 1
            [comment_author] => author-two
            [comment_author_email] => curtis+authortwo@curtismchale.ca
            [comment_author_url] => 
            [comment_author_IP] => 192.168.50.1
            [comment_date] => 2014-05-30 04:47:56
            [comment_date_gmt] => 2014-05-30 04:47:56
            [comment_content] => Now author two is making a comment.
            [comment_karma] => 0
            [comment_approved] => 1
            [comment_agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4
            [comment_type] => 
            [comment_parent] => 0
            [user_id] => 3
        )

)

Now if we pass an array like that through array_rand all we’re going to get back is the keys for the comments. That means before we send the comments out of the filter back to WordPress we’d need to reassemble the array. If we only sent the keys back then WordPress would give us an error since it expects to see an array of objects when it builds out the comments.

Using shuffle means that the array stays intact and is just random. Nothing left for us to do but head off to the next coding challenge for the day.

Screen Shot 2014-06-27 at 12.43.57 PM

Randomizing WooCommerce Reviews

By default the WooCommerce reviews are going to display in the order they were submitted to your site. That can present a few issues though.

The last (or first) review is what everyone will see. What if we want to randomize the rievews for the product so that each time a user visits the page they will see a different review in the first position?

Today I’m going to show you how.

Getting our Comments

The first thing we need to recognize is that WooCommerce stores the reviews as WordPress commments. That means we can look at the filters that are provided for comments in WordPress.

Checking out the WordPress filters page the one we want is comments_array. We can find usage of this filter in the comments_template function.

Here is what a few lines of the function look like.

/**
 * Filter the comments array.
 *
 * @since 2.1.0
 *
 * @param array $comments Array of comments supplied to the comments template.
 * @param int   $post_ID  Post ID.
 */
$wp_query-&gt;comments = apply_filters( 'comments_array', $comments, $post-&gt;ID );

We can see that it takes the comments in our $wp_query object and passes them to our filter along with the $post->ID that goes with the post WordPress is currently getting comments for.

Randomizing our WooCommerce Reviews

Now that we know where we need to hook to get our comments lets look at our code to randomize the comments.

class SFN_Random_Comments{
 
    function __construct(){
 
        add_filter( 'comments_array', array( $this, 'randomize_comments' ), 10, 2 );
 
    } // construct
 
    /**
     * Randomizes the reviews for a WooCommerce site
     *
     * @since 1.0
     * @author SFNdesign, Curtis McHale
     *
     * @param array         $comments       required            The array of comments
     * @param int           $post_id        requride            The post_id we are getting comments for
     */
    public function randomize_comments( $comments, $post_id ){
 
        if ( get_post_type( $post_id ) !== 'product' ) return $comments;
 
        shuffle( $comments );
 
        return $comments;
 
    } // randomize_comments
 
} // SFN_Random_Comments
 
new SFN_Random_Comments();

First we have a class and then we call our filter from inside the __construct function.

Then our main function uses get_post_type to make sure that we are currently on a product. If we are not on a product then just return the $comments without altering them.

If we are on a product then we use the PHP function shuffle. shuffle simply randomizes the array. We don’t need to set another variable or anything. Just return $comments after shuffle and we have our reviews randomized.

You can go get the plugin on Github.

Screencast

Screen Shot 2014-07-02 at 9.19.10 AM

Getting Started with WordPress Development

So you’ve heard about this WordPress thing and you want to get started do you? The question is…

Where on earth do you start?

The first question to ask yourself is why do you want to work with WordPress anyway? Is it because you actually believe the platform is awesome or do you just see that lots of people use it and thus there may be some money to be made?

I’m not here to judge your reasons, but if you just think there is money to be made are you really going to stick it out long term?

Now on to what I think a WordPress developer should know to really have a decent handle on WordPress.

Queries

A solid WordPress developer should have a decent handle on:

So now you have links for the major ways to get post data out of WordPress but when do you use them?

WP_Query

I use WP_Query when I have multiple queries on a page or if we need extra data on the front-page.php template of the site. I almost never use it when I need to return HTML (when using get_the_title() in a function say).

get_posts

In plugins I’m usually using get_posts and passing the post_id in to my functions then returning HTML.

get_post

Used to get a single post by the post_id.

$custom_post = get_post( 32 );

Now $custom_post is a single post object for the post with the post_id 32. I use this sometimes but not very often.

pre_get_posts

pre_get_posts is great when you need to exclude a single category from the usual WordPress archive page queries. Running a custom WP_Query there is just going to get you in a world of pain.

It’s also great for modifying your RSS feeds.

function change_rss( $query ){
 
    if ( ! is_feed() ) return $query;
 
    $query->set( 'posts_per_page', 100 );
 
} // change_rss
add_action( 'pre_get_posts', 'change_rss' );

The above code makes my RSS feed have 100 posts but leaves the rest of my stock WordPress queries alone.

The Loop

The WordPress loop is in your template files usually and looks like:

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
 
    <!-- post content goes here -->
    <?php the_title(); ?>
 
<?php endwhile; else: ?>
 
    <h3>Oops!! Looks like something went wrong. Please get in touch with the site <a href="mailto:<?php echo get_bloginfo('admin_email'); ?>">administrator</a> and we'll get this sorted out</h3>
 
<?php endif; ?>
 
<?php wp_reset_postdata(); ?>

Notice I’m using wp_reset_postdata() right at the bottom. That’s going to get your $wp_query global all cleaned up for you at the end so that your loops don’t conflict with each other.

If you want to pass a custom WP_Query to your loop then it would look like:

$args = array(
    'post_type'      => 'custom_post_type',
    'posts_per_page' => '20'
);
 
$custom_loop = new WP_Query( $args );
 
if ( $custom_loop->have_posts() ) : while ( $custom_loop->have_posts() ) : $custom_loop->the_post(); ?>
 
    <!-- post content goes here -->
    <?php the_title(); ?>
 
<?php endwhile; else: ?>
 
    <h3>Oops!! Looks like something went wrong. Please get in touch with the site <a href="mailto:<?php echo get_bloginfo('admin_email'); ?>">administrator</a> and we'll get this sorted out</h3>
 
<?php endif; ?>
 
<?php wp_reset_postdata(); ?>

Now my loop has my custom post content showing up in it instead of the default WordPress query content.

WP_User_Query

Similar to WP_Query but for getting users out of the WordPress database.

$args = array(
    'role' => 'Administrator'
);
 
$admins = new WP_User_Query( $args );
 
foreach ( $admins->results as $users ){
    echo $users->display_name;
}

The above code is going to get me all the users that are WordPress Administrators and echo out their display_name.

Actions and Filters

Our second big idea is WordPress actions and filters.

Actions are fired when something happens in WordPress. Like our pre_get_posts action above. I happens just before WordPress actually gets our posts from the database but after the Query parameters have been set.

Filters always pass data in and allow us to change the data as it passes through. A great example is changing the stock email address that WordPress sends all emails from.

function change_email( $email ){
    return 'your@email.com';
}
add_filter( 'wp_mail_from', 'change_email' );

Our code above changes the stock email to your@email.com so all emails going out of WordPress will be from that new email address.

For more on actions and filters you should check out Pippin’s Plugin 101 course (linked later) or my book (linked later).

enqueue

No we don’t just go around throwing our JS and CSS files in script tags in our headers and footers. WordPress has a built in system to enqueue your scripts/styles for you properly.

This is going to help make sure that you don’t end up with 9 versions of jQuery messing each other up. If you’re adding a script to WordPress you should be using wp_enqueue_script hooked to wp_enqueue_scripts.

Styles should be added with wp_enqueue_style and also hooked to wp_enqueue_scripts.

If you’re adding scripts or styles to the WordPress admin use the same functions but hook to admin_enqueue_scripts.

Make sure you limit your admin scripts to only the page you need them on.

AJAX

Sure there are a bunch of ways to make an AJAX call that can work, but WordPress has a set of hooks built in just for making AJAX calls.

As usual if WordPress has a default way to do something you should just stick with that way.

Not sure how to do that yet well I’ve got great post for you.

Seperation of code for theme/plugin

I’ve actually written a whole blog post on this before so I’ll summarize now and you can go read it all.

If a feature is likely to live past the current visual style of your site then it should be in a plugin.

Build a Widget

Yes you should know how to build a WordPress Widget (and what one is).

And check out how to build a widget.

Build a Shortcode

Shortcodes can be very useful to get advanced content/layouts in WordPress.

You should know how to build one if you want to know WordPress. Check out the Plugin Development 101 link later for some training on building a shortcode.

Escape all the things

The first 2 rules of dealing with users on the web are:

Never trust the user

Same goes for rule 5 and 8.

To that end you need to escape and sanitize input/output in the database. WordPress comes with a bunch of escaping functions that you should be using first. Then see what PHP has. Then get fancy and write your own escaping functions.

Theme Review Guidelines

“Hey” you say, I’m going to build plugins not themes so I can skip the Theme Review Guidelines.

No you can’t.

Your plugins are still going to have to interface with WordPress themes.

You will have to dig in to a theme for client work and make some changes.

You should know what the best practices are in building a theme.

Who to Follow

So there is some foundational knowledge for getting to know WordPress

Tom McFarlin

Tom is a solid WordPress developer with a background in a few other languages. His main blog covers business, WordPress and life. He’s done an excellent series lately on the (philosophy on WordPress](http://tommcfarlin.com/wordpress-philosophy-the-vocal-minority/).

Tom is also the WordPress editor for code.tutsplus.com.

Pippin

Pippin is a very smart developer and the founder of Easy Digital Downloads. He writes tutorials on WordPress pretty consistently and if you’re looking at getting in to plugin development he has a great Plugin Development 101 series.

Don’t forget to listen to Apply Filters which is a developer focused WordPress podcast.

Paulund

Paul Underwood has been killing it lately with great tutorials on how to build WordPress properly. Look through the site and make note of all the awesome resources.

Brad Touesnard

Brad focuses on a few things, not just WordPress tutorials. He has a great view of the WordPress landscape from code to building a product.

One of his posts that I adopted right away was this one about extending WP_Query.

Curtis McHale

Yeah that’s me and I write on my personal site about business mainly.

This is WP Theme Tutorial and here I write about building things with WordPress.

I also wrote a book called Becoming a WordPress Development Professional that covers most of the content above in more depth. It’s meant as a guideline for you to start your journey to becoming awesome with WordPress.

WHOA!!!

Yeah that’s a lot that I really think a WordPress developer should know. Seems pretty daunting of you’re just starting out doesn’t it.

WordPress isn’t just some ‘easy’ thing you can pick up and be awesome at. It takes some real work. I think that a solid developer from another language is likely to be able to get most of this in a few months of projects.

Sure there will still be edge cases they don’t quite know how to handle but they’ll be a decent developer that’s not writting terrible code.

If you’re just starting remember I was just starting to and I wrote bad code. You’re going to write bad code as well. No way around that.

Find a code mentor that’s willing to do some reviews for you. Send your first 5 projects to them and ask about what your doing and a critique.

You’ll learn way faster that way.