<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kevin Bradwick</title>
	<atom:link href="http://www.kevinbradwick.co.uk/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kevinbradwick.co.uk</link>
	<description>Web development and design blog</description>
	<lastBuildDate>Tue, 09 Mar 2010 11:13:20 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>WordPress Skeleton Theme for Developers</title>
		<link>http://www.kevinbradwick.co.uk/2010/03/wordpress-skeleton-theme-for-developers/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/03/wordpress-skeleton-theme-for-developers/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 11:13:20 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=148</guid>
		<description><![CDATA[If you are a frequent WP developer then you&#8217;ll know the time consuming job of setting up a completely new theme. Today I have created a basic theme that is free to use for anyone creating a new site. It features a developer options page in the Appearance Panel where you can configure such things [...]]]></description>
			<content:encoded><![CDATA[<p>If you are a frequent WP developer then you&#8217;ll know the time consuming job of setting up a completely new theme. Today I have created a basic theme that is free to use for anyone creating a new site. It features a developer options page in the Appearance Panel where you can configure such things as thumbnails, javascript and contact page generation.</p>
<p>The in built contact page features field verification and csrf security prevention.</p>
<p>You can download or clone a copy of the theme by visiting the <a href="http://github.com/kbradwick/WPSkeletonTheme">Git Repo</a> at <a href="http://www.github.com">GitHub</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/03/wordpress-skeleton-theme-for-developers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL JOIN statements</title>
		<link>http://www.kevinbradwick.co.uk/2010/02/sql-join-statements/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/02/sql-join-statements/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 16:35:01 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[join]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=122</guid>
		<description><![CDATA[When working with relational databases, you will often find that the data your require can reside on multiple tables. In simple terms, a join statement brings together these tables using a common identifier. Joins are the best way to query a database whilst improving transaction efficiency.
An example
We&#8217;ll create two tables, a users and account table. [...]]]></description>
			<content:encoded><![CDATA[<p>When working with relational databases, you will often find that the data your require can reside on multiple tables. In simple terms, a join statement brings together these tables using a common identifier. Joins are the best way to query a database whilst improving transaction efficiency.</p>
<h3>An example</h3>
<p>We&#8217;ll create two tables, a users and account table. The account table holds account names (e.g. Guest, Admin, Super Admin) and the users table contains one foreign key that relates to the account type.</p>
<p>The structure looks like this;</p>
<p><img class="alignnone size-thumbnail wp-image-123" title="schema" src="http://www.kevinbradwick.co.uk/wp-content/uploads/2010/02/schema.png" alt="" /></p>
<p>And here&#8217;s some data that we&#8217;ll work with&#8230;</p>
<p><img class="alignnone size-thumbnail wp-image-126" title="sql-select-accounts" src="http://www.kevinbradwick.co.uk/wp-content/uploads/2010/02/sql-select-accounts.png" alt="" /></p>
<p><img class="alignnone size-full wp-image-130" title="users-data" src="http://www.kevinbradwick.co.uk/wp-content/uploads/2010/02/users-data.png" alt="" width="392" height="161" /></p>
<p>Now to perform a simple join of these two tables, we could use&#8230;</p>
<p><img class="alignnone size-full wp-image-132" title="join-1" src="http://www.kevinbradwick.co.uk/wp-content/uploads/2010/02/join-1.png" alt="" width="544" height="139" /></p>
<p>This simple query will return all records but using this as a base we can further customise our query to make it more useful. If we wanted to find out all users who have Admin accounts we&#8217;d use this statement.</p>
<pre name="code" class="php">
SELECT * FROM users u LEFT JOIN accounts a ON u.account_type = a.id WHERE a.name = 'Admin'

// or we could use the ID from the accounts table

SELECT * FROM users u LEFT JOIN accounts a ON u.account_type = a.id WHERE a.id = 2
</pre>
<h3>A note on usage</h3>
<p>This is MySQL join in its most simplest terms. You can join multiple tables providing you database design allows it. So with that cleared up, if you are not already using joins in your queries, start using them and cut down on the number of transactions you make&#8230;it&#8217;ll also make your life a lot easier retrieving data.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/02/sql-join-statements/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Calculating the radius of a coordinate using PHP</title>
		<link>http://www.kevinbradwick.co.uk/2010/01/calculating-the-radius-of-a-coordinate-using-php/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/01/calculating-the-radius-of-a-coordinate-using-php/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:23:11 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[radius]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=59</guid>
		<description><![CDATA[Sometimes you&#8217;ll want to get the radius of a location. For this I have written a function that I&#8217;ll share with you.
/*

Copyright 2009  Kevin Bradwick (http://www.kevinbradwick.co.uk) 

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 [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes you&#8217;ll want to get the radius of a location. For this I have written a function that I&#8217;ll share with you.</p>
<pre name="code" class="php">/*

Copyright 2009  Kevin Bradwick (http://www.kevinbradwick.co.uk) 

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.
*/

function geoRadius($lat, $lng, $rad, $kilometeres = FALSE, $out = 'OBJ')
	{
		$radius = ($kilometers) ? ($rad * 0.621371192) : $rad;

		(float)$dpmLAT = 1 / 69.1703234283616; 

		// Latitude calculation
		(float)$usrRLAT = $dpmLAT * $radius;
		(float)$latMIN = $lat - $usrRLAT;
		(float)$latMAX = $lat + $usrRLAT;

		// Longitude calculation
		(float)$mpdLON = 69.1703234283616 * cos($lat * (pi/180));
		(float)$dpmLON = 1 / $mpdLON; // degrees per mile longintude
		$usrRLON = $dpmLON * $radius;
		$lonMIN = $lng - $usrRLON;
		$lonMAX = $lng + $usrRLON;

		$output = array("lonMIN" =&gt; $lonMIN, "lonMAX" =&gt; $lonMAX, "latMIN" =&gt; $latMIN, "latMAX" =&gt; $latMAX);
		return ($out == 'OBJ') ? (object)$output : $output;
	}
</pre>
<p>Okay, so how do you use it? Well, the first two paramaters are the Latitude and Longitude of your origin. The third is the radius ammount. If you want to calculate it based on kilometers (default is in miles), enter a boolean value of TRUE for the fourth parameter. The last paramter specifices the output method, by default it will return an object, enter anything other that OBJ here will return an associative array.</p>
<p>So, once we&#8217;ve done that what do we have? You will then have 4 variables to use, each of them minimum and maximum values of Latitude and Longitude. This is how you could use the output of this function to query a database that contains geo data.</p>
<pre name="code" class="php">$radius = geoRadius(53.741690, -2.397750, 48.28032);
$sql = "SELECT * FROM `mytable` WHERE `latitude` BETWEEN {$radius-&gt;latMIN} AND {$radius-&gt;latMAX} AND `longitude` BETWEEN {$radius-&gt;lonMIN} AND {$radius-&gt;lonMAX}";
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/01/calculating-the-radius-of-a-coordinate-using-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating custom page layouts with WordPress</title>
		<link>http://www.kevinbradwick.co.uk/2010/01/creating-custom-page-layouts-with-wordpress/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/01/creating-custom-page-layouts-with-wordpress/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:14:13 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=52</guid>
		<description><![CDATA[If you are using WordPress to create a website, and you want to avoid it looking like a blog, then you need to look at creating custom pages.
The &#8216;page.php&#8217; file displays your generic page content using this template. You could customise this page to make it look as you want and set it as the [...]]]></description>
			<content:encoded><![CDATA[<p>If you are using WordPress to create a website, and you want to avoid it looking like a blog, then you need to look at creating custom pages.</p>
<p>The &#8216;page.php&#8217; file displays your generic page content using this template. You could customise this page to make it look as you want and set it as the home page in the WordPress admin screen. But what happens if we want to create a contact form section? or maybe a one off page that contains some bespoke code? This is how you do it&#8230;</p>
<p>In your theme folder, create a new file. In this example, I&#8217;ll call it custom.php. Now insert the following code to it.</p>
<pre name="code" class="php">&lt;?php
/*
Template Name: Custom Page Layout
*/
get_header(); ?&gt;;

// enter your code here

&lt;?php get_footer(); ?&gt;</pre>
<p>Now with that, create a new page in the admin section and on the right where it says &#8220;Attributes&#8221;, select the template name you wrote in the custom.php (Custom Page Layout in this case).</p>
<p>Easy <img src='http://www.kevinbradwick.co.uk/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/01/creating-custom-page-layouts-with-wordpress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fixing the pagination error using custom permalink structure in WordPress</title>
		<link>http://www.kevinbradwick.co.uk/2010/01/fixing-the-pagination-error-using-custom-permalink-structure-in-wordpress/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/01/fixing-the-pagination-error-using-custom-permalink-structure-in-wordpress/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:11:33 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[pagination]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=50</guid>
		<description><![CDATA[For a while this has been really annoying me. Whenever I use the permalink structure /%category%/%postname%/ I get 404 errors when using the next_posts_link() and previous_posts_link(). I a fix for this here. You can use this code in a themes functions.php file or use it as a plugin.
/*
Plugin Name: Fix Paging in Category Listings
Plugin URI: [...]]]></description>
			<content:encoded><![CDATA[<p>For a while this has been really annoying me. Whenever I use the permalink structure /%category%/%postname%/ I get 404 errors when using the next_posts_link() and previous_posts_link(). I a fix for this <a href="http://barefootdevelopment.blogspot.com/2007/11/fix-for-wordpress-paging-problem.html">here</a>. You can use this code in a themes functions.php file or use it as a plugin.</p>
<pre name="code" class="php">/*
Plugin Name: Fix Paging in Category Listings
Plugin URI: http://www.thinkbarefoot.com
Description: Fixes a bug where next/previous links are broken in category by year/month listings
Version: 0.5
Author: Doug Smith
Author URI: http://www.thinkbarefoot.com

Copyright 2007  Doug Smith  (email: dsmith@thinkbarefoot.com)

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.

*/

/**
 * Function to fix problem where next/previous buttons are broken on list
 * of posts in a category when the custom permalink string is:
 * /%category%/%year%/%monthnum%/%postname%/
 * The problem is that with a url like this:
 *
 * /category/2007/10/page/2
 *
 * the 'page' looks like a post name, not the keyword "page"
 */
function remove_page_from_query_string($query_string)
{
    if ($query_string['name'] == 'page' &amp;&amp; isset($query_string['page'])) {
        unset($query_string['name']);
        // 'page' in the query_string looks like '/2', so split it out
        list($delim, $page_index) = split('/', $query_string['page']);
        $query_string['paged'] = $page_index;
    }
    return $query_string;
}

add_filter('request', 'remove_page_from_query_string');</pre>
<p>Happy days.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/01/fixing-the-pagination-error-using-custom-permalink-structure-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Grant remote access to MySQL</title>
		<link>http://www.kevinbradwick.co.uk/2010/01/grant-remote-access-to-mysql/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/01/grant-remote-access-to-mysql/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:05:32 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[remote access]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=45</guid>
		<description><![CDATA[If you&#8217;ve installed MySQL on a machine other than your work station (localhost) and you want to use the MYSQL GUI tools to administer the server then you&#8217;ll need to grant remote acccess on the root account (or another account that has useful privileges. This is how to do it.
Connect via SSH and login to [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve installed MySQL on a machine other than your work station (localhost) and you want to use the MYSQL GUI tools to administer the server then you&#8217;ll need to grant remote acccess on the root account (or another account that has useful privileges. This is how to do it.</p>
<p>Connect via SSH and login to mysql&#8230;</p>
<pre name="code" class="php">
# mysql -u root -p

// now grant access for the root account
mysql > GRANT USAGE ON *.* to root@'%' IDENTIFIED BY '[password]';
mysql > FLUSH PRIVILEGES;
</pre>
<p>Change [password] to whatever the password is for the root account and include the single quotes (&#8216;). The % sign means it will accept connections from this account on all IP addresses. You could if you wanted, choose a specific IP here.</p>
<p>You&#8217;ll now be able to login remotely.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/01/grant-remote-access-to-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to install LibSSH2 on a CentOS 5 system</title>
		<link>http://www.kevinbradwick.co.uk/2010/01/how-to-install-libssh2-on-a-centos-5-system/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/01/how-to-install-libssh2-on-a-centos-5-system/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:02:54 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[CentOS]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[libssh2]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=42</guid>
		<description><![CDATA[Libssh2 is a C library implementing the SSH2 protocol for PHP. Here&#8217;s how to get it installed on a CentOS system running PHP 5.1.6.
First step is to install some dependencies.

yum install automake php-devel libtool openssl-devel gcc++ gcc

Next, let&#8217;s download the latest release of libssh2 (1.2.2 at time of writing), unpack and install

# cd /tmp
# wget [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.libssh2.org/">Libssh2</a> is a C library implementing the SSH2 protocol for PHP. Here&#8217;s how to get it installed on a CentOS system running PHP 5.1.6.</p>
<p>First step is to install some dependencies.</p>
<pre name="code" class="php">
yum install automake php-devel libtool openssl-devel gcc++ gcc
</pre>
<p>Next, let&#8217;s download the latest release of libssh2 (1.2.2 at time of writing), unpack and install</p>
<pre name="code" class="php">
# cd /tmp
# wget http://www.libssh2.org/download/libssh2-1.2.2.tar.gz
# tar -zxf libssh2-1.2.2.tar.gz
# cd libssh2-1.2.2
# ./configure
# make all install
# cd ..
# rm -rf libssh2-1.2.2
</pre>
<p>Now we need to install the PHP bindings.</p>
<pre name="code" class="php">
# cd /usr/lib/php (for x64 systems use /usr/lib64/php)
# wget http://pecl.php.net/get/ssh2-0.11.0.tgz
# tar -zxf ssh2-0.11.0.tgz
# cd ssh2-0.11.0
# phpize &amp;&amp; ./configure –with-ssh2 &amp;&amp; make
# cd modules
# mv ssh2.so /usr/lib/php/modules/ssh2.so
# cd /usr/lib/php
# rm -rf ssh2-0.11.0
# rm -f ssh2-0.11.0.tgz
</pre>
<p>Update the PHP ini file and add the ssh2.so extension like this.</p>
<pre name="code" class="php">
# vi /etc/php.ini
extension_dir = “/usr/lib/php/modules”
extension = ssh2.so
</pre>
<p>Now restart apache and everything should be installed. You can test it my looking at the out put of <strong># php -m</strong> that will list all of the installed modules.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/01/how-to-install-libssh2-on-a-centos-5-system/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Accessing a timezone list in PHP</title>
		<link>http://www.kevinbradwick.co.uk/2010/01/accessing-a-timezone-list-in-php/</link>
		<comments>http://www.kevinbradwick.co.uk/2010/01/accessing-a-timezone-list-in-php/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 17:40:33 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[timezone]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=37</guid>
		<description><![CDATA[The php function timezone_identifiers_list is only available in newer versions of PHP (typically 5.2+), although the php site suggests it is available in versions 5.1.0+. If you have CentOS (current version 5.4) or another linux distro that has 5.1.6 or similar, you may find the function does not exist.
For these situations, I have compiled a [...]]]></description>
			<content:encoded><![CDATA[<p>The php function <a href="http://php.net/manual/en/function.timezone-identifiers-list.php">timezone_identifiers_list</a> is only available in newer versions of PHP (typically 5.2+), although the php site suggests it is available in versions 5.1.0+. If you have CentOS (current version 5.4) or another linux distro that has 5.1.6 or similar, you may find the function does not exist.</p>
<p>For these situations, I have compiled a list of timezones as an array that you can use in you projects. Either cut and paste the code below or <a href="http://www.kevinbradwick.co.uk/getfile/2" title="PHP Timezone List">download</a> the file.</p>
<pre name="code" class="php">
/**
 * Timezone List
 * Array compiled from timezone_identifiers_list() that can be used on
 * systems that do not support the function but a list of timezones is
 * needed anyway
 **/

$timezone = array(
		'Africa/Abidjan',
		'Africa/Accra',
		'Africa/Addis_Ababa',
		'Africa/Algiers',
		'Africa/Asmara',
		'Africa/Bamako',
		'Africa/Bangui',
		'Africa/Banjul',
		'Africa/Bissau',
		'Africa/Blantyre',
		'Africa/Brazzaville',
		'Africa/Bujumbura',
		'Africa/Cairo',
		'Africa/Casablanca',
		'Africa/Ceuta',
		'Africa/Conakry',
		'Africa/Dakar',
		'Africa/Dar_es_Salaam',
		'Africa/Djibouti',
		'Africa/Douala',
		'Africa/El_Aaiun',
		'Africa/Freetown',
		'Africa/Gaborone',
		'Africa/Harare',
		'Africa/Johannesburg',
		'Africa/Kampala',
		'Africa/Khartoum',
		'Africa/Kigali',
		'Africa/Kinshasa',
		'Africa/Lagos',
		'Africa/Libreville',
		'Africa/Lome',
		'Africa/Luanda',
		'Africa/Lubumbashi',
		'Africa/Lusaka',
		'Africa/Malabo',
		'Africa/Maputo',
		'Africa/Maseru',
		'Africa/Mbabane',
		'Africa/Mogadishu',
		'Africa/Monrovia',
		'Africa/Nairobi',
		'Africa/Ndjamena',
		'Africa/Niamey',
		'Africa/Nouakchott',
		'Africa/Ouagadougou',
		'Africa/Porto-Novo',
		'Africa/Sao_Tome',
		'Africa/Tripoli',
		'Africa/Tunis',
		'Africa/Windhoek',
		'America/Adak',
		'America/Anchorage',
		'America/Anguilla',
		'America/Antigua',
		'America/Araguaina',
		'America/Argentina/Buenos_Aires',
		'America/Argentina/Catamarca',
		'America/Argentina/Cordoba',
		'America/Argentina/Jujuy',
		'America/Argentina/La_Rioja',
		'America/Argentina/Mendoza',
		'America/Argentina/Rio_Gallegos',
		'America/Argentina/Salta',
		'America/Argentina/San_Juan',
		'America/Argentina/San_Luis',
		'America/Argentina/Tucuman',
		'America/Argentina/Ushuaia',
		'America/Aruba',
		'America/Asuncion',
		'America/Atikokan',
		'America/Bahia',
		'America/Barbados',
		'America/Belem',
		'America/Belize',
		'America/Blanc-Sablon',
		'America/Boa_Vista',
		'America/Bogota',
		'America/Boise',
		'America/Cambridge_Bay',
		'America/Campo_Grande',
		'America/Cancun',
		'America/Caracas',
		'America/Cayenne',
		'America/Cayman',
		'America/Chicago',
		'America/Chihuahua',
		'America/Costa_Rica',
		'America/Cuiaba',
		'America/Curacao',
		'America/Danmarkshavn',
		'America/Dawson',
		'America/Dawson_Creek',
		'America/Denver',
		'America/Detroit',
		'America/Dominica',
		'America/Edmonton',
		'America/Eirunepe',
		'America/El_Salvador',
		'America/Fortaleza',
		'America/Glace_Bay',
		'America/Godthab',
		'America/Goose_Bay',
		'America/Grand_Turk',
		'America/Grenada',
		'America/Guadeloupe',
		'America/Guatemala',
		'America/Guayaquil',
		'America/Guyana',
		'America/Halifax',
		'America/Havana',
		'America/Hermosillo',
		'America/Indiana/Indianapolis',
		'America/Indiana/Knox',
		'America/Indiana/Marengo',
		'America/Indiana/Petersburg',
		'America/Indiana/Tell_City',
		'America/Indiana/Vevay',
		'America/Indiana/Vincennes',
		'America/Indiana/Winamac',
		'America/Inuvik',
		'America/Iqaluit',
		'America/Jamaica',
		'America/Juneau',
		'America/Kentucky/Louisville',
		'America/Kentucky/Monticello',
		'America/La_Paz',
		'America/Lima',
		'America/Los_Angeles',
		'America/Maceio',
		'America/Managua',
		'America/Manaus',
		'America/Marigot',
		'America/Martinique',
		'America/Mazatlan',
		'America/Menominee',
		'America/Merida',
		'America/Mexico_City',
		'America/Miquelon',
		'America/Moncton',
		'America/Monterrey',
		'America/Montevideo',
		'America/Montreal',
		'America/Montserrat',
		'America/Nassau',
		'America/New_York',
		'America/Nipigon',
		'America/Nome',
		'America/Noronha',
		'America/North_Dakota/Center',
		'America/North_Dakota/New_Salem',
		'America/Panama',
		'America/Pangnirtung',
		'America/Paramaribo',
		'America/Phoenix',
		'America/Port-au-Prince',
		'America/Port_of_Spain',
		'America/Porto_Velho',
		'America/Puerto_Rico',
		'America/Rainy_River',
		'America/Rankin_Inlet',
		'America/Recife',
		'America/Regina',
		'America/Resolute',
		'America/Rio_Branco',
		'America/Santarem',
		'America/Santiago',
		'America/Santo_Domingo',
		'America/Sao_Paulo',
		'America/Scoresbysund',
		'America/Shiprock',
		'America/St_Barthelemy',
		'America/St_Johns',
		'America/St_Kitts',
		'America/St_Lucia',
		'America/St_Thomas',
		'America/St_Vincent',
		'America/Swift_Current',
		'America/Tegucigalpa',
		'America/Thule',
		'America/Thunder_Bay',
		'America/Tijuana',
		'America/Toronto',
		'America/Tortola',
		'America/Vancouver',
		'America/Whitehorse',
		'America/Winnipeg',
		'America/Yakutat',
		'America/Yellowknife',
		'Antarctica/Casey',
		'Antarctica/Davis',
		'Antarctica/DumontDUrville',
		'Antarctica/Mawson',
		'Antarctica/McMurdo',
		'Antarctica/Palmer',
		'Antarctica/Rothera',
		'Antarctica/South_Pole',
		'Antarctica/Syowa',
		'Antarctica/Vostok',
		'Arctic/Longyearbyen',
		'Asia/Aden',
		'Asia/Almaty',
		'Asia/Amman',
		'Asia/Anadyr',
		'Asia/Aqtau',
		'Asia/Aqtobe',
		'Asia/Ashgabat',
		'Asia/Baghdad',
		'Asia/Bahrain',
		'Asia/Baku',
		'Asia/Bangkok',
		'Asia/Beirut',
		'Asia/Bishkek',
		'Asia/Brunei',
		'Asia/Choibalsan',
		'Asia/Chongqing',
		'Asia/Colombo',
		'Asia/Damascus',
		'Asia/Dhaka',
		'Asia/Dili',
		'Asia/Dubai',
		'Asia/Dushanbe',
		'Asia/Gaza',
		'Asia/Harbin',
		'Asia/Ho_Chi_Minh',
		'Asia/Hong_Kong',
		'Asia/Hovd',
		'Asia/Irkutsk',
		'Asia/Jakarta',
		'Asia/Jayapura',
		'Asia/Jerusalem',
		'Asia/Kabul',
		'Asia/Kamchatka',
		'Asia/Karachi',
		'Asia/Kashgar',
		'Asia/Kathmandu',
		'Asia/Kolkata',
		'Asia/Krasnoyarsk',
		'Asia/Kuala_Lumpur',
		'Asia/Kuching',
		'Asia/Kuwait',
		'Asia/Macau',
		'Asia/Magadan',
		'Asia/Makassar',
		'Asia/Manila',
		'Asia/Muscat',
		'Asia/Nicosia',
		'Asia/Novosibirsk',
		'Asia/Omsk',
		'Asia/Oral',
		'Asia/Phnom_Penh',
		'Asia/Pontianak',
		'Asia/Pyongyang',
		'Asia/Qatar',
		'Asia/Qyzylorda',
		'Asia/Rangoon',
		'Asia/Riyadh',
		'Asia/Sakhalin',
		'Asia/Samarkand',
		'Asia/Seoul',
		'Asia/Shanghai',
		'Asia/Singapore',
		'Asia/Taipei',
		'Asia/Tashkent',
		'Asia/Tbilisi',
		'Asia/Tehran',
		'Asia/Thimphu',
		'Asia/Tokyo',
		'Asia/Ulaanbaatar',
		'Asia/Urumqi',
		'Asia/Vientiane',
		'Asia/Vladivostok',
		'Asia/Yakutsk',
		'Asia/Yekaterinburg',
		'Asia/Yerevan',
		'Atlantic/Azores',
		'Atlantic/Bermuda',
		'Atlantic/Canary',
		'Atlantic/Cape_Verde',
		'Atlantic/Faroe',
		'Atlantic/Madeira',
		'Atlantic/Reykjavik',
		'Atlantic/South_Georgia',
		'Atlantic/St_Helena',
		'Atlantic/Stanley',
		'Australia/Adelaide',
		'Australia/Brisbane',
		'Australia/Broken_Hill',
		'Australia/Currie',
		'Australia/Darwin',
		'Australia/Eucla',
		'Australia/Hobart',
		'Australia/Lindeman',
		'Australia/Lord_Howe',
		'Australia/Melbourne',
		'Australia/Perth',
		'Australia/Sydney',
		'Europe/Amsterdam',
		'Europe/Andorra',
		'Europe/Athens',
		'Europe/Belgrade',
		'Europe/Berlin',
		'Europe/Bratislava',
		'Europe/Brussels',
		'Europe/Bucharest',
		'Europe/Budapest',
		'Europe/Chisinau',
		'Europe/Copenhagen',
		'Europe/Dublin',
		'Europe/Gibraltar',
		'Europe/Guernsey',
		'Europe/Helsinki',
		'Europe/Isle_of_Man',
		'Europe/Istanbul',
		'Europe/Jersey',
		'Europe/Kaliningrad',
		'Europe/Kiev',
		'Europe/Lisbon',
		'Europe/Ljubljana',
		'Europe/London',
		'Europe/Luxembourg',
		'Europe/Madrid',
		'Europe/Malta',
		'Europe/Mariehamn',
		'Europe/Minsk',
		'Europe/Monaco',
		'Europe/Moscow',
		'Europe/Oslo',
		'Europe/Paris',
		'Europe/Podgorica',
		'Europe/Prague',
		'Europe/Riga',
		'Europe/Rome',
		'Europe/Samara',
		'Europe/San_Marino',
		'Europe/Sarajevo',
		'Europe/Simferopol',
		'Europe/Skopje',
		'Europe/Sofia',
		'Europe/Stockholm',
		'Europe/Tallinn',
		'Europe/Tirane',
		'Europe/Uzhgorod',
		'Europe/Vaduz',
		'Europe/Vatican',
		'Europe/Vienna',
		'Europe/Vilnius',
		'Europe/Volgograd',
		'Europe/Warsaw',
		'Europe/Zagreb',
		'Europe/Zaporozhye',
		'Europe/Zurich',
		'Indian/Antananarivo',
		'Indian/Chagos',
		'Indian/Christmas',
		'Indian/Cocos',
		'Indian/Comoro',
		'Indian/Kerguelen',
		'Indian/Mahe',
		'Indian/Maldives',
		'Indian/Mauritius',
		'Indian/Mayotte',
		'Indian/Reunion',
		'Pacific/Apia',
		'Pacific/Auckland',
		'Pacific/Chatham',
		'Pacific/Easter',
		'Pacific/Efate',
		'Pacific/Enderbury',
		'Pacific/Fakaofo',
		'Pacific/Fiji',
		'Pacific/Funafuti',
		'Pacific/Galapagos',
		'Pacific/Gambier',
		'Pacific/Guadalcanal',
		'Pacific/Guam',
		'Pacific/Honolulu',
		'Pacific/Johnston',
		'Pacific/Kiritimati',
		'Pacific/Kosrae',
		'Pacific/Kwajalein',
		'Pacific/Majuro',
		'Pacific/Marquesas',
		'Pacific/Midway',
		'Pacific/Nauru',
		'Pacific/Niue',
		'Pacific/Norfolk',
		'Pacific/Noumea',
		'Pacific/Pago_Pago',
		'Pacific/Palau',
		'Pacific/Pitcairn',
		'Pacific/Ponape',
		'Pacific/Port_Moresby',
		'Pacific/Rarotonga',
		'Pacific/Saipan',
		'Pacific/Tahiti',
		'Pacific/Tarawa',
		'Pacific/Tongatapu',
		'Pacific/Truk',
		'Pacific/Wake',
		'Pacific/Wallis',
		'UTC');
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2010/01/accessing-a-timezone-list-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Foreign Key tutorial using MySQL</title>
		<link>http://www.kevinbradwick.co.uk/2009/12/foreign-key-tutorial-using-mysql/</link>
		<comments>http://www.kevinbradwick.co.uk/2009/12/foreign-key-tutorial-using-mysql/#comments</comments>
		<pubDate>Tue, 29 Dec 2009 06:16:13 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[foreign key]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=27</guid>
		<description><![CDATA[Foreign key&#8217;s in a database table reference primary key&#8217;s of rows in another table. Why does this matter? Well, if you have ever built an application whereby updating/deleting a row from one table requires further transactions to be carried out on other, you&#8217;ll know the nuisance and confusion this can cause (especially if your application [...]]]></description>
			<content:encoded><![CDATA[<p>Foreign key&#8217;s in a database table reference primary key&#8217;s of rows in another table. Why does this matter? Well, if you have ever built an application whereby updating/deleting a row from one table requires further transactions to be carried out on other, you&#8217;ll know the nuisance and confusion this can cause (especially if your application is reasonably large). Foreign key&#8217;s can rescue the situation&#8230;</p>
<h3>A typical example</h3>
<p>Let&#8217;s say you have a table called users and a user_meta table that holds multiple rows relating to the user table (use the following code in a test database).</p>
<pre name="code" class="php">
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(30) DEFAULT NULL,
  `passwd` varchar(40) DEFAULT NULL,
  `email` varchar(120) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

CREATE TABLE `user_meta` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `meta_name` varchar(255) DEFAULT NULL,
  `meta_value` text,
  `user_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_user_meta_users` (`user_id`),
  CONSTRAINT `FK_user_meta_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;
</pre>
<p>Notice the database engine in this case is InnoDB. MyISAM does not support foreign keys. Without worrying too much about the syntax, the user_meta table declares that the column &#8216;user_id&#8217; is foreign and references the primary key on the user table (id).</p>
<p>Now that they foreign keys are referenced what can we do with them? In this case, I&#8217;ve declared ON DELETE CASCADE. This means that if we delete a user from the user table, all user_meta records for that user will be deleted automatically. If we said on UPDATE CASCADE, should the users primary key get updated, then the user_meta table will automatically update the user_id column as necessary.</p>
<p>There are other options too&#8230;</p>
<p>RESTRICT and NO ACTION. These two are similar in that nothing will get changed upon the parent table changing. SET NULL will set the foreign key table column values to null upon delete/update. SET DEFAULT can also come in handy if you have set default values to columns.</p>
<p>Try them out, if you havent&#8217; used them before, I will guarantee you will find uses for them in the future.</p>
<p>Download sample database dump: <a class="downloadlink" href="http://www.kevinbradwick.co.uk/getfile/1" title=" downloaded 20 times" >Foreign Key Tutorial SQL Dump (20)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2009/12/foreign-key-tutorial-using-mysql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Introduction to Doctrine&#8217;s Nested Set</title>
		<link>http://www.kevinbradwick.co.uk/2009/12/introduction-to-doctrines-nested-set/</link>
		<comments>http://www.kevinbradwick.co.uk/2009/12/introduction-to-doctrines-nested-set/#comments</comments>
		<pubDate>Tue, 29 Dec 2009 02:31:30 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[nested set]]></category>

		<guid isPermaLink="false">http://www.kevinbradwick.co.uk/?p=24</guid>
		<description><![CDATA[A common problem requirement among developers is to store hierarchial data to a flat file database. You have probably come across the parent/child methodology whereby a table has a typical &#8216;parent&#8217; column that references, you guessed it, its parent &#8211; this method is also known as adjacency list model. Querying in this way can often [...]]]></description>
			<content:encoded><![CDATA[<p>A common <del datetime="2010-01-07T21:13:33+00:00">problem</del> requirement among developers is to store hierarchial data to a flat file database. You have probably come across the parent/child methodology whereby a table has a typical &#8216;parent&#8217; column that references, you guessed it, its parent &#8211; this method is also known as adjacency list model. Querying in this way can often lead to a lot of transactions per single request (one transaction per row) and an application with many rows of hierarchial data will dent your transaction efficiency. There are benefits to this system (simpler SQL management for CRUD operation) but there is another way that is more efficient &#8211; the Nested Set model (also known as modified pre-order tree traversal algorithm &#8211; MPTT).</p>
<p>Doctrine has by far the best known method of Nested Set operation I have come across. My main reason for this is the ability for it to store multiple roots in a single table. For example, this could come in handy for an application that implements multiple blogs.</p>
<p>So, let&#8217;s dive in and take a look at how we use this system.</p>
<p>For this example, I am going to show how this would come in use for a web application that hosts multiple blogs. Our database schema looks like this.</p>
<pre name="code" class="php">
CREATE TABLE `Blogs` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `blog_admin` int(11) unsigned NOT NULL,
  `blog_title` text,
  `blog_description` text,
  `status` enum('live','private','offline') NOT NULL DEFAULT 'live',
  PRIMARY KEY (`id`),
  KEY `FK_Blogs_users` (`blog_admin`),
  CONSTRAINT `FK_Blogs_users` FOREIGN KEY (`blog_admin`) REFERENCES `Users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;

CREATE TABLE `BlogCategories` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `blog_id` int(11) unsigned NOT NULL,
  `name` varchar(140) DEFAULT NULL,
  `description` text,
  `lft` int(11) unsigned NOT NULL,
  `rgt` int(11) unsigned NOT NULL,
  `level` int(10) unsigned NOT NULL DEFAULT '0',
  `root_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_BlogCategories_blogs` (`blog_id`),
  CONSTRAINT `FK_BlogCategories_blogs` FOREIGN KEY (`blog_id`) REFERENCES `Blogs` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;
</pre>
<p>Simply, we have a blogs table that has a foreign key back to a user table and the BlogCategories table will hold our categories relating to all the blogs. Next we will create our models&#8230;</p>
<p>I&#8217;m not going to cover model creation but for reference, I prefer to design my database through MySQL Query Browser first, then let Doctrine to generate the models for me using the generateModelsFromDb method like this;</p>
<pre name="code" class="php">
//...
Doctrine_Core::generateModelsFromDb('/path/to/where/i/want/to/output/my/models/', array('doctrine'), array('generateTableClasses' => true));
</pre>
<p>In my BlogsCategories model, I tell it to act as a nested set, tell it there are many roots and declare the root column name (by default it is root_id, so I don&#8217;t need to declare it here but this is to show you how you would declare it to be a name other than root_id)</p>
<pre name="code" class="php">
//...
$this->actAs('NestedSet', array(
	'hasManyRoots' => TRUE,
	'rootColumnName' => 'root_id'
	));
</pre>
<p>Now we&#8217;re good to go. The first thing we want to do is create a new root category. This category will be the parent category for all other categories.</p>
<pre name="code" class="php">
$root = new BlogCategories();
$root->blog_id = 1; // relates back to the Blogs table
$root->name = 'ROOT';
$root->description = 'DO NOT DELETE';
$root->save(); // saves the new root category

$treeObject = Doctrine_Core::getTable('BlogCategories')->getTree();
$treeObject->createRoot($root);
</pre>
<p>If you look in your database now, you notice this has inserted the row and set the left/right/level/root columns appropriately. You can do this a few times to create a few roots. Now let&#8217;s look at inserting nodes for a particular blog. We&#8217;ll work with blog_id 1.</p>
<pre name="code" class="php">
$blogOneRoot = Doctrine_Core::getTable('BlogCategories')->find(1); // the primary key of the root node for blog_id 1

$child = new BlogCategories();
$child->blog_id = 1;
$child->name = 'Doctrine';
$child->description = 'Good Doctrine tutorials';
$child->getNode()->insertAsLastChildOf($blogOneRoot);
</pre>
<p>This has now inserteda new child category for blog_id 1.</p>
<p>The last step we&#8217;ll take now is rendering a category tree of a particular blog. For this example again, I&#8217;ll assume blog_id 1.</p>
<pre name="code" class="php">
$categoryTree = Doctrine_Core::getTable('BlogCategories')->getTree();
foreach($categoryTree ->fetchTree(array('root_id' => 1)) as $cat)
{
     echo str_repeat(' ', $cat['level']) . $cat['name'] . "\n";

}
</pre>
<p>And that pretty much covers the basics. It&#8217;s really easy &#8211; once you know how. There are many more cool things that can be done with Doctrine&#8217;s nested set method that I may cover on another day. Have fun!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kevinbradwick.co.uk/2009/12/introduction-to-doctrines-nested-set/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
