Alex Biddle     About     Archive     Feed

Create bullet-proof integration tested WordPress plugins with CakePHP

First copy WordPress in a new directory that is being served by your webserver. Next create your WordPress database to use, along with the test bed that will be used by CakePHP.

mysql -u YOURUSERNAME -p YOURPASSWORD
CREATE DATABASE wordpress;
CREATE DATABASE wordpress_tests;

Edit your WordPress configuration with the above details (you don’t have to use wordpress_tests at the moment). You’re just doing a regular WordPress install.

The next step will largely depend on your URL scheme. For now, we’ll create a folder simply called “cake”. After installing CakePHP, change the database configuration file with the details of the two databases that you created earlier. At this point what you should have is a WordPress install with a CakePHP installation under the /cake directory. For our simple application, we’ll create a page that adds a WordPress user, if they give the page a specific password. This will then be unit-tested using CakePHP. To do this, we’ll create a Memberships controller. Often it is easier to type in the URL first, to make sure that CakePHP is working: http://testing.local/cake/memberships/add gives, “Error: Create the class MembershipsController below in file: app/Controller/MembershipsController.php”. So we create this class:

<?php
class MembershipsController extends AppController {
    public function add() {

        
    }

}

Now we get with “http://testing.local/cake/memberships/add”, “Error: The view for MembershipsController::add() was not found.” So lets add the view:

alex@monolith:~/dev/testing/cake/app/View$ mkdir Memberships 
alex@monolith:~/dev/testing/cake/app/View$ touch Memberships/add.ctp
<?php echo $this->Form->create('Membership'); ?>
<?php echo $this->Form->input('username'); ?>
<?php echo $this->Form->input('password'); ?>
<?php echo $this->Form->input('secret'); ?>
<?php echo $this->Form->submit(); ?>
<?php echo $this->Form->end(); ?>

Then we want to create a simple Model for our data validation, so that the view can work.

alex@monolith:~/dev/testing/cake/app/Model$ touch Membership.php
<?php 
App::uses('AppModel', 'Model');
class Membership extends AppModel {

    public $useTable = false;
	public $validate = array(
        'username' => array(
            'required' => array(
                'rule' => 'notEmpty',
                'message' => 'Please enter a username'
            ),
        ),
        'password' => array(
            'required' => array (
                'rule' => 'notEmpty',
                'message' => 'Password is required',
                'required' => true
            ),
        ),
        'secret' => array(
            'required' => array (
                'rule' => 'notEmpty',
                'message' => 'Secret is required',
                'required' => true
            ),
            'check_secret' => array(
                'rule' => array('checkSecret'),
                'message' => 'Your secret is not correct'
            ),
            
        ),
    );

    public function checkSecret($check) {
        return $check['secret'] == 'foobar';
    }
}

Now for the interesting part. By providing CakePHP with details of the underlying database tables, it can dynamically (re-)create the entire database for use in unit tests. I’ve already created the ORM models, building on an earlier project, that you can use in your project. You can also populate the tables with seed data using CakePHP’s fixtures. The only caveat is that you can’t use expect WordPress’s own database functions to connect to the dynamic database that CakePHP creates. They will connect to the database configuration as specified in your wp-config.php file. However in most situations, you can work around this.

So how do you mesh WordPress and CakePHP without namespace conflicts? The answer is to add WordPress at the following points:

alex@monolith:~/dev/testing/cake/app/webroot$ ack-grep "wp-blog-header"
index.php
4:require_once '../../../wp-blog-header.php';

test.php
4:require_once '../../../wp-blog-header.php';

You can check it works with a simple test. Firstly, login to WordPress, secondly add this line to your add.ctp page:

<?php print_r(wp_get_current_user()); ?>

You should see details of your current user display on screen. If this works, then you can use all the WordPress functions from within your CakePHP application. To connect to the WordPress database, however, you need to create models that tell CakePHP how to interact with the WordPress database. This is a time-consuming process, so helpfully I’ve already done this work for you.

alex@monolith:~/dev/testing/cake/app/Plugin$ git clone git@github.com:alexbiddle/cakephp-wordpress-plugin.git
alex@monolith:~/dev/testing/cake/app/Plugin$ cd cakephp-wordpress-plugin/
alex@monolith:~/dev/testing/cake/app/Plugin/cakephp-wordpress-plugin$ mv Wordpress/ ..

And make sure that the plugin is loaded with the following line to your bootstrap.php file:

CakePlugin::load('Wordpress'); 

Now let’s get to the interesting bit and create our application that registers a new WordPress user if they know the secret password. Let’s return to our MembershipsController and add the following:

<?php

App::uses('AppController', 'Controller');
App::uses('Membership', 'Model');

class MembershipsController extends AppController {
    
    public function add() {
    $this->loadModel('Wordpress.User');

        if ($this->request->is('post')) {           

            $this->Membership->set($this->request->data);

            if ($this->Membership->validates()) {
                $username = $this->request->data['Membership']['username'];
                $password = $this->request->data['Membership']['password'];

                $this->User->clear();
                $user_data = array(
                    'User' => array(
                        'user_login' => $username,
                        'user_pass' => wp_hash_password($password),
                        'user_nicename' => $username,
                        'user_email' => '',
                        'user_url' => '',
                        'user_registered' => date('Ymd'),
                        'user_activation_key' => '',
                        'user_status' => '0',
                        'display_name' => $username
                    ),
                    'Usermetum' => array(
                        array(
                            'meta_key' => 'wp_capabilities',
                            'meta_value' => maybe_serialize(array('contributor' => true)),
                        )
                    )
                );

                if ($this->User->saveAll($user_data)) {
                    $this->Session->setFlash('Congratulations, you are now registered!');
                    return $this->redirect('/memberships/add');
                } else {
                    $this->Session->setFlash('Sorry, could not register!');
                    return $this->redirect('/memberships/add');                    
                }
            }
        }        
    }
}

The code should be fairly self-explanatory if you have used CakePHP before. We just use some extra WordPress functions, so that the user is automatically added to be a “Contributor”, and of course use the WordPress password hashing function. Note that we are not using WordPress’s wpdb object to save to the database, so it will work with CakePHP’s dynamic fixtures. So let’s have a go at unit testing this. (If you don’t have phpunit installed, follow my earlier guide).

<?php
App::uses('MembershipsController', 'Controller');

/**
 * MembershipsController Test Case
 *
 */
class MembershipsControllerTest extends ControllerTestCase {

/**
 * Fixtures
 *
 * @var array
 */
	public $fixtures = array (
        'plugin.wordpress.post',
        'plugin.wordpress.user',
        'plugin.wordpress.usermetum',
        'plugin.wordpress.postmetum',
        'plugin.wordpress.termrelationship',
        'plugin.wordpress.comment',
	);

    public function testCreateNewUser() {
        $this->generate('Memberships', array(
            'methods' => array(),
            'components' => array()
        ));

        $data = array(
            'Membership' => array(
                'username' => 'Foobar',
                'password' => 'Baz',
                'secret' => 'foobar'
            )
        );
   
        $result = $this->testAction('/memberships/add', array('method' => 'POST', 'return' => 'vars', 'data' => $data));

        $this->controller->User->recursive = -1;
        $this->assertNotEmpty($this->controller->User->find(
            'first', array(
                'conditions' => array(
                    'user_login' => 'Foobar'
                )
            )
        ), 'Must have created a new user!');
    }


    public function testIncorrectSecret() {
        global $wpdb;
        $wpdb->check_connection();

        $this->generate('Memberships', array(
            'methods' => array(),
            'components' => array()
        ));

        $data = array(
            'Membership' => array(
                'username' => 'Foobar',
                'password' => 'Baz',
                'secret' => 'Incorrect'
            )
        );
        
        $result = $this->testAction('/memberships/add', array('method' => 'POST', 'return' => 'vars', 'data' => $data));
        $this->assertEquals($this->controller->Membership->validationErrors['secret'][0], 'Your secret is not correct');        
    }   
}

Here we have two unit tests: one that tests a successful membership account creation, the other checks that our validation is working. Its quite neat as through our fixtures, CakePHP can create an entirely new WordPress installation with each test function. There is nothing here that is really new if you are familiar to CakePHP unit testing, apart from the following:

        global $wpdb;
        $wpdb->check_connection();

This is used as WordPress loses track of its own database connection between the tests. You shouldn’t need it unless it is too awkward to create a CakePHP abstraction for everything that WordPress does. I was using a third party library that created codes, and checked the database to make sure that its code was unique, which created an error, as it was losing the connection.

This meant that my code was not completely isolated, but I was OK with this trade-off. You can actually remove these two lines if you want, but I thought I would keep them in just in case you found yourself scratching your head, trying to work out why your first test ran perfectly, when all the other tests fall over.

So there you go. Now you can create bullet-proof WordPress plugins using all the functionality you know in CakePHP. I can imagine this might come in handy when you need to do some heavy-lifting using CakePHP, then just displaying the results with a traditional WordPress plugin.