Unit Testing Best Practices – Getting Started with Unit Testing in WordPress Part 3

We’re almost ready to dive in to actually writing some unit tests, but we still have 2 best practices to cover. Yeah I know we’ve talked about a lot before writing code, but a proper understanding of best practices is going to make the whole writing code part so much easier.

Other Posts in the Series

Only one assertion per test

Yup that’s right, you should only have 1 assertion for each test written in 99% of cases. If you’re function does 2 things, then write 2 tests for the function not one big test that does has 2 assertions testing the 2 different parts of the functions.

But why only a single assertion per test???

A test should only fail for a single reason, not many possible reason inside a test. Sticking with a single assertion per test makes sure that this happens. It lets you write singularly focused code and you can tell right away where your code is having an issue because the test only has 1 reason to fail.

Your test name should be precise and clear. If you’re testing A and B in a test, what do you call it that’s precise and clear? Sticking with a single assertion per test helps make it all easier.

Tests should be concise and readable. So you setup your test data (we’ll cover how) then test the outcome of your function with an assertion then your test is done. No 30 line tests that do 3 things, you’re just going to confuse yourself.

All of the above points can be traced back to the long term maintainability of your test suit. If you don’t make it easy to read/understand for future you (who is not nearly as smart as current coffee fueled you) then your tests are simply going to lapse and become totally useless.

Now at the end let my remind you that this is a ‘best practice’ not a hard fast rule. Your default way to write a test should be to write a single assertion per test and very occasionally you’re going to find a reason not to do it.

First, stop and take a look at the base code. Can you simplify/refactor it so you can get to a single assertion? If so, go ahead and write the tests needed then refactor the code to match the tests.

If you can’t put in 2 assertions maybe even 3. Remember that the more complex you make your test to read/understand the higher the bar is raised for long term maintainability.

Test the expected and unexpected

Our second best practice is to not only test the expected output, but what does your code do with bad data?

If you need an email in the function what happens if you don’t get an email?

What if you’re supposed to email a password to a user and the email provided doesn’t actually match a user in your system? Should it still be sent or should we stop our process?

Do your tests cover those scenarios?

The point is that you’re not just going to have a 1:1 ratio for test:functions. You’re likely to have many more tests than functions, at least 2 tests for every 1 function in your code.

Next time we’ll actually start digging in to writing some tests for our plugins.

Screen Shot 2014-08-22 at 10.47.07 AM

Tools of the Unit Testing Trade

This is the second post in a series on Unit Testing. In the first post we covered the basics of what unit testing is and why you’d write tests.

Today we’re going to cover some of the tools you’ll hear about in Unit Testing for WordPress. We may not cover each one of them in our series, but you should be aware of what they are and how you could use them.

PHPUnit

PHPUnit is a PHP unit testing framework. It’s what WordPress uses as the testing framework so it’s what you should be using for your projects.

Starting with PHPUnit gives us a bunch of functions to leverage to write our tests like:

  • assertTrue <- checks if something is true like $this->assertTrue( $a === $b ) which would make sure that $a and $b are the same thing
  • assertArrayHasKey <- checks in the given array has a key like $this->assertArrayHasKey( ‘foo’, array( ‘foo’ => ‘bar’ );
  • assertNull <- checks if the given variable is NULL like $this->assertNull( ‘foo’ ) would fail because ‘foo’ is not NULL
  • And lots of others which you can find in the PHPUnit Documentation.

WP_UnitTestCase

So we need PHPUnit which can deal with all our testing needs but is going to create a bunch of extra work which we don’t really need to do.

See WordPress has a special class built in to the WordPress develop build that extends the normal PHPUnit_Framework_TestCase provided in PHPUnit. This class gives us the ability to do a bunch of stuff through it’s factory method like:

  • Create a user for a test
  • Create a post for a test
  • Create a taxonomy or term for a test
  • Create sites for Multisite in our test

Using this API we can create posts/pages/users to run our code against. An example workflow would be mocking up a test of wp_insert_post like this.

<?php
 
/**
 * Simple extended test class testing wp_insert_post
 */
class Test_Posts_Insert extends WP_UnitTestCase {
 
    /**
     * to understand setUp, view Setup_Teardown_Example.php
     */
    function setUp() {
        parent::setUp();
 
        $this->author = new WP_User( $this->factory->user->create( array( 'role' => 'editor' ) ) );
 
        $post = array(
            'post_author'  => $this->author->ID,
            'post_status'  => 'publish',
            'post_content' => rand_str(),
            'post_title'   => rand_str(),
        );
 
        // insert a post
        $this->post_id = wp_insert_post($post);
    }
 
    /**
     * to understand tearDown, view Setup_Teardown_Example.php
     */
    function tearDown() {
        parent::tearDown();
        wp_delete_post( $this->post_id );
    }
 
    /**
     * Verify that if 'updating' a post w/o the required data, it is considered empty and returns 0.
     * The post is considered "empty" if both:
     * 1. The post type supports the title, editor, and excerpt fields
     * 2. The title, editor, and excerpt fields are all empty
     */
    function test_insert_post_returns_0() {
 
        $post = get_post( $this->post_id );
 
        $updated_id = wp_insert_post( array(
            'ID'           => $this->post_id,
            'post_name'    => 'post_before_post_name',
        ) );
 
        $this->assertEquals( 0, $updated_id );
 
    }
 
    /**
     * Verify that if 'updating' a post w/ the required data, it returns the post ID indicating success
     */
    function test_insert_post_returns_id() {
 
        $post = get_post( $this->post_id );
 
        $updated_id = wp_insert_post( array(
            'ID'           => $this->post_id,
            'post_name'    => 'post_before_post_name',
            'post_content' => 'filler',
        ) );
 
        $this->assertEquals( $updated_id, $this->post_id );
 
    }
 
}

Here we use the setUp method to create a post and then we test wp_insert_post to make sure that it fails first and secondly that it returns the expected post_id so it doesn’t fail.

A likely scenario for your plugin/theme work would be to create a post and save a meta_value to it. Then check to make sure that the post shows up using get_posts querying for the meta_value.

I say don’t use the default PHPUnit class for your WordPress tests, leverage the WordPress WP_UnitTestCase to get a bunch of power for free.

Big Note: NEVER run the full WordPress test suite on a live or development site. The WordPress test suite expects certain data and will delete your database and add new data to get the expected data. That means you’d loose your site data which is bad.

WP Mock

WP Mock is a way to mock up your code for testing. It would be used instead of the WordPress WP_UnitTestCase class inside WordPress.

It should never be used to run the core WordPress tests though.

For you’re plugin you need to include WP_Mock with composer and make sure that it’s autoloader properly.

Then you can use WP_Mock to mimic the WordPress API. Instead of hitting the database with WP_UnitTestCase (which is slower and then makes your tests rely on all of the WordPress dependencies and internals) you get WP_Mock to take over.

This is going to make your tests faster.

Unfortunately there isn’t much written about how to use WP_Mock, and using it is simply harder to understand when you start with unit testing than the WP_UnitTestCase factory method.

I recommend that you get comfortable with unit testing using WP_UnitTestCase then start diving in to WP_Mock.

WP CLI

WP CLI is nothing short of awesome anyway so you should use it, but it can also help you get started with unit testing much faster.

WP CLI gives you a bunch of WordPress commands on your server which means you can do stuff like upgrade all your plugins with a single terminal command.

Or you can generate 100 (or 10000) posts with a single terminal command.

WP CLI also has built in commands called scaffold that can scaffold your plugin testing suite for you and have it all setup properly. Simply rename a single file to match your base plugin name and get writing tests.

Actually you don’t even need to do the renaming, but it’s best practice to rename things so do it.

By default with WP CLI scaffold PHPUnit will run all php files in your tests directory that start with test-*. This is just the default setting and you can change it to whatever you want.

I always just leave the defaults intact because I’ve never been given a good reason to do otherwise.

WP CLI is not required to run unit tests, it just automates the setup of unit tests in your plugin.

VVV

Now installing all of the above is a bit of work on your Mac or Windows machine and is something I haven’t done. I use Vagrant and VVV as my development environment and it has PHPUnit, WP_UnitTestCase, and WP CLI installed for me, which make everything so much easier.

I’m down with easy.

If you’re new to VVV then here are some great resources:

If you’re on a Mac and running MAMP then here is a great tutorial on how to setup PHPUnit from Tom Mcfarlin.

If you’re on Windows running WAMP all the tutorials said to use PEAR which is no longer supported by PHPUnit and will be shut down December 2014. If you have resources to use for WAMP send them my way and I’ll include them.

Travis CI

Travis CI is a hosted continuous testing solution for all projects (private and public) hosted on Github. Sorry no BitBucket or Beanstalk support yet.

The core to grasp when starting with Travis is the CI portion which stands for continuous integration. Each time you commit and push to your Github master branch Travis is going to check your code and run all your tests.

You can choose to run your tests against different versions of PHP and for WordPress projects you can even define the versions of WordPress that you want to run the tests against.

From the Travis interface you can even check pull requests to see if they pass your unit tests. This also shows up in a basic form on the Github pull request.

WP CLI will give you a basic .travis.yml file to start your build process with but you’ll need to do a few more tweaks to get it going.

Travis also lets you put little badges on your Github repository to show how the tests are running with the current code. Below you can see an example of the Travis build for the WP Rest API.

WP API Travis Badge

We’ll cover how to setup Travis-CI with our project later in the series.

That’s it for today, next time we’ll start writing a plugin and write our tests as we go.

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.