Archive for the ‘Open Source’ Category

A step in the right direction

Tuesday, July 15th, 2008

At my current workplace, on a particular project we have not been allowed to use third party libraries, due to fears of licensing issues if and when we come to sell the product. The project is predominantly for the business, but it would be nice if we could convert the product into something we could market.

Despite multiple discussions and suggesting to management that open source software licensed under an MIT or BSD license would not have any implications, they would not have it. This has meant writing pretty much everything we need from scratch.

Recently, the managing director must have noticed the improved the user experience the little bit of JavaScript we use brings to the product. In previous conversations, I’ve mentioned to the MD that JavaScript is an area that the development team has little experience with, and this seems to have nudged him into letting us use a third party library or tool kit within the product. I spent a bit of time reviewing some of the frameworks and tool kits out there and basically boiled down to going with Dojo. Most of the frameworks reviewed provide similar features, Dojo came up on top because of the BSD license, recent announcement of a partnership with the Zend Framework and the added bonus of contributors having to sign a CLA. Looking forward to getting stuck into it and making our application a little easier on the eye.

The next task is to go about documenting the best way to include and use the tool kit in our application, will write about it if I get chance.

Fat Models and the Data Access Layer

Tuesday, June 17th, 2008

There’s been some discussion recently on why active record:

  1. Sucks
  2. Sucks - but not like that;
  3. Doesn’t suck

I like the active record pattern, so I don’t think it sucks, but I do think it’s used a little out of context sometimes.

If you’re building a small lightweight app, then I think using your Data access layer as the M in MVC is a logical thing to do. It’s quick, it’s easy and you can extend either your active record in Rails, or extend your Table DataGateway in the Zend Framework and you wont go far wrong.

As soon as your app gets a little more complex, you might want to start creating custom
models that contain more business logic than simply pulling and pushing to the database. If your application is complex enough, chances are your model will need to interact with more than one database table, if not database, so at this point, like Bill Karwin pointed out, your model should be using the DAL, not being the DAL. Loosening the coupling between model and DAL, should also help with automated testing the business logic, in that mock objects could replace the DAL.

The only problem is, I don’t know the best way to do it.

I’m currently learning the ways of the Zend Framework and would be interested to see how people think the best way to implement this kind of complex model. I’m currently leaning toward something like this. I’ve included a Zend_Form object, to show how the Persons model encapsulates more logic than just pushing to and from the database. I think the biggest benefit of Zend_Form is validating input, which I consider domain logic, so should be part of the model. But I’m not sure the best way to make things easily testable, without pushing into the realms of fancy Dependency Injection and what not, which I’m not all that familiar with.

File: application/models/Persons.php

<?php

class Persons
{
    public function findByEmail($email)
    {
        $table  = self::getTable();
        $select = $table->select()
                        ->where('email = ?', $email);
        return $table->fetchAll($select);
    }

    public static function getTable()
    {
        // add some dependency injection?
        return new Persons_Table();
    }   

    public static function getForm()
    {
        // add some dependency injection?
        return new Persons_Form();
    }

    // ...
}

File: application/models/Persons/Table.php

<?php

class Persons_Table extends Zend_Db_Table
{
    protected $_name = 'persons';

    // ...
}

File: application/models/Persons/Form.php

<?php

class Persons_Form extends Zend_Form
{
    // ...
}

At first it may seem that the Persons model just ends up acting as a proxy to the Persons_Form and Persons_Table, but once you start writing methods that use both together, you’ll start seeing fatter models and thin controllers, which is all good.

This really is a request for comments really, as I’m personally not sure about the best way to go about this. Would be interesting if any of the people using the MVC part of the Zend Framework in the real world go about this?

How To: Simple database migrations with Phing and DbDeploy

Monday, April 14th, 2008

Introduction

This How To will introduce some simple database migrations to your PHP application. Ruby on Rails is a popular web application framework, that provides a method of migrating (upgrading) the applications database programatically, keeping the database schema essentially version controlled. This allows individual developers to update their working databases and the databases on testing, staging or production machines to be updated with new versions of applications. The CakePHP framework has recently developed a migrations library simliar to rails, but this article focuses on using seperate tools to run database migrations, a build tool called Phing, along with a method for creating database migrations, dbdeploy.

Install Phing

I always use the beta or release candidate of phing and for the purposes of this article I suggest you do too. The best way to download and install phing is using PEAR. This can be done on Linux or Windows assuming you have the pear script in your PATH with three shell commands.

shell> pear channel-discover pear.phing.info
shell> pear config-set preferred_state beta
shell> pear install phing/phing

Example Application structure

As an example, we’re going to develop a simple application with the following directory structure.

example/
 |-- db/
 |   `-- deltas/
 |-- deploy/
 |   `-- scripts/
 |-- library/
 `-- public/

The db directory contains sql files for using and manipulating our database and
the deploy directory contains our build scripts that set the migrations in motion. The library directory contains our application code and the public folder will contain scripts and files accessible directly from the web, but will not be the focus of this article.

Build scripts

This section shows you how to develop the build scripts that will run the database migrations. The first file we need to create is a simple configuration file and should be fairly self explanatory. The file is written as key=value, lines beginning with a # are comments. Open your editor and save the following text as deploy/build.properties.

# Property files contain key/value pairs
#key=value

# This dir must contain the local application
build.dir=../

# Credentials for the database migrations
db.host=localhost
db.user=user
db.pass=password
db.name=example

# paths to programs
progs.mysql=/usr/bin/mysql

The next file we are going to create is the deploy/build.xml file. This is the file that tells Phing what we want it to do. I’m not going to go into too much detail describing each part of the build file, there are some comments, but you should consult the Phing Documentation for further details and enhancements.

<?xml version="1.0" ?>
<project name="PurpleMonkey" basedir="." default="build">

    <!-- Sets the DSTAMP, TSTAMP and TODAY properties -->
    <tstamp/>

    <!-- Load our configuration -->
    <property file="./build.properties" />

    <!-- create our migration task -->
    <target name="migrate" description="Database Migrations">  

        <!-- load the dbdeploy task -->
        <taskdef name="dbdeploy" classname="phing.tasks.ext.dbdeploy.DbDeployTask"/>

        <!-- these two filenames will contain the generated SQL to do the deploy and roll it back-->
        <property name="build.dbdeploy.deployfile" value="deploy/scripts/deploy-${DSTAMP}${TSTAMP}.sql" />
        <property name="build.dbdeploy.undofile" value="deploy/scripts/undo-${DSTAMP}${TSTAMP}.sql" />

        <!-- generate the deployment scripts -->
        <dbdeploy
            url="mysql:host=${db.host};dbname=${db.name}"
            userid="${db.user}"
            password="${db.pass}"
            dir="${build.dir}/db/deltas"
            outputfile="${build.dir}/${build.dbdeploy.deployfile}"
            undooutputfile="${build.dir}/${build.dbdeploy.undofile}" />

        <!-- execute the SQL - Use mysql command line to avoid trouble with large files or many statements and PDO -->
        <exec
            command="${progs.mysql} -h${db.host} -u${db.user} -p${db.pass} ${db.name} &lt; ${build.dbdeploy.deployfile}"
            dir="${build.dir}"
            checkreturn="true" />
    </target>
</project>

That’s essentially all the magic we need. Now we just need to create our database.

Writing dbdeploy delta scripts

We haven’t actually created our database, so rather than create it the traditional way, we will actually use the migrations to create the initial schema. We’ve not actually decided what our example application does yet, but seeing as most tutorials make blogs, why don’t we give that a bash. We’ll start simple, one table with three columns called post.

Field Type Comment
title VARCHAR(255) The title of our post
time_created DATETIME The time we created our post
content MEDIUMTEXT The content of our post

Dbdeploy works by creating numbered delta files. Each delta files contains simple SQL to both deploy the change and roll it back. The basic layout of a delta file is like so.

--//

-- Run SQL to do the changes

--//@UNDO

-- RUN SQL to undo the changes

--//

We are creating our initial schema, so put the following content in db/deltas/1-create_initial_schema.sql

--//

CREATE TABLE `post` (
    `title` VARCHAR(255),
    `time_created` DATETIME,
    `content` MEDIUMTEXT,
);

--//@UNDO

DROP TABLE `post`;

--//

Migrating the database

We are one step away from running our first migration. To keep track of the current version of the database, dbdeploy requires a table in the database. This is the only time we will have to interact with the mysql client directly.

shell> mysql -hlocalhost -uroot -ppassword example
mysql> CREATE TABLE changelog (
  change_number BIGINT NOT NULL,
  delta_set VARCHAR(10) NOT NULL,
  start_dt TIMESTAMP NOT NULL,
  complete_dt TIMESTAMP NULL,
  applied_by VARCHAR(100) NOT NULL,
  description VARCHAR(500) NOT NULL
);
mysql> ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number, delta_set);

We are now ready to run our first migration and create the initial schema for our application.

shell>cd deploy
shell>phing migrate

All being well, we now have a posts table in our database. But what about an author for our blog posts? We’ll have to add another table and a foreign key from the post table to author table. To do this we create another delta, we call this one db/deltas/2-create_author_and_link_to_post.sql

--//

CREATE TABLE `author` (
    `author_id` INT(10) unsigned auto_increment,
    `name` VARCHAR(255),
    PRIMARY KEY (`author_id`)
);

ALTER TABLE `post` ADD `author_id` INT(10) unsigned NULL;

--//@UNDO

ALTER TABLE `post` DROP `author_id`;

DROP TABLE `author`;

--//

Run our migrations again.

shell> cd deploy
shell> phing migrate

Conclusion

That’s pretty much it, you’ve seen how to create database deltas and use them to migrate your database, if you can’t be bothered to copy and paste things to try for yourself, download the example application.

There are plenty of caveats when it comes to version controlling databases, especially if you branch and merge your application code, some are detailed in the dbdeploy documentation

This tutorial is probably incomplete or wrong in plenty of ways, if you think you have something to point out, please leave your comments below

Contributing to Open Source Software

Thursday, April 3rd, 2008

I’m a big fan of Open Source Software. Although I’m sure I could get by if I had to move to proprietary based software, at the present time, open source software earns me a living. Because of this, at the start of the year, I decided to give something back and start contributing to open source. I chose the Zend Framework, because it is written in PHP and is a project I have a lot of interest in. I registered for an account on the bug tracker and signed the CLA, but that’s as far as I got. I started looking through some of the bugs available for fixing, but found that the simpler ones were being dealt with quickly by other people and the only ones left where a little too complex for me. I still plan to contribute before the year is over. I intend to use the framework for a project I have in mind and the frameworks current implementation of a client for Amazon Web Services needs adding to for the purposes I need, so maybe I can contribute that way.

Back to the point, today I think I found a bug in PHPUnit, I couldn’t work out how to submit a bug report via the website, so I emailed Sebastian Bergmann with a patch. I’m not going to describe the bug until I know for sure that I’m correct, but I’m at least 90% sure I’m in the right. So, if I am right, that’s my first solid contribution to open source software.

Update

Well it seems I was right in reporting the bug, but it appears Sebastian didn’t like the patch I sent, or more likely it didn’t work. This changeset looks slightly different to the patch I submitted.

Index: PHPUnit/Util/Metrics/Function.php
===================================================================
--- PHPUnit/Util/Metrics/Function.php   (revision 2707)
+++ PHPUnit/Util/Metrics/Function.php   (working copy)
@@ -449,7 +449,7 @@
         }

         else {
-            $this->crap = pow($this->ccn, 2) * (pow(1 - $this->coverage/100, 3) + $this->ccn);
+            $this->crap = pow($this->ccn, 2) * pow(1 - $this->coverage/100, 3) + $this->ccn;
         }
     }

From what I can tell, the change put into place is also incorrect according to the C.R.A.P. index formula.

Update 2

I emailed Sebastian and he fixed it properly in changeset 2712