<?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>Coding My Thoughts</title> <atom:link href="http://marianoiglesias.com.ar/feed/" rel="self" type="application/rss+xml" /><link>http://marianoiglesias.com.ar</link> <description>A glimpse at a coder&#039;s troubled mind</description> <lastBuildDate>Wed, 06 Jan 2010 19:25:16 +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>Building a blog with Lithium and Doctrine</title><link>http://marianoiglesias.com.ar/li3-lithium/building-a-blog-with-lithium-and-doctrine/</link> <comments>http://marianoiglesias.com.ar/li3-lithium/building-a-blog-with-lithium-and-doctrine/#comments</comments> <pubDate>Sat, 02 Jan 2010 21:47:38 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[Lithium]]></category> <category><![CDATA[doctrine]]></category> <category><![CDATA[li3]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/?p=137</guid> <description><![CDATA[<p>This post aims to be a very basic introduction to the world of <a
href="http://li3.rad-dev.org">Lithium</a>, also known as <a
href="http://li3.rad-dev.org">li3</a>. As such, it is mostly based on the <a
href="http://rad-dev.org/lithium_examples">ubiquitous blog sample</a>. The idea is to learn, through the source&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/cakephp/pagination-with-custom-find-types-in-cakephp/' rel='bookmark' title='Permanent Link: Pagination with custom find types in CakePHP'>Pagination with custom find types in CakePHP</a></li></ol>]]></description> <content:encoded><![CDATA[<p>This post aims to be a very basic introduction to the world of <a
href="http://li3.rad-dev.org">Lithium</a>, also known as <a
href="http://li3.rad-dev.org">li3</a>. As such, it is mostly based on the <a
href="http://rad-dev.org/lithium_examples">ubiquitous blog sample</a>. The idea is to learn, through the source code, the basic notions on Lithium and its integration with the <a
href="http://www.doctrine-project.org/">Doctrine ORM mapper</a> to build a very basic blog application. Unlike the blog example at rad-dev.org, this example is based on <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en">Doctrine 2.0</a>, which is (at the time of this writing) in Alpha stage, and is built for PHP 5.3. After this tutorial, you should be able to jump start and build your own li3 powered applications.</p><p>Before we proceed, a disclaimer. This tutorial shows a rather quick and dirty integration with Doctrine (as you&#8217;ll see it&#8217;s all mostly done in a base model class), while the most clean approach would be to implement all this as an extension, using Datasources and Query parsing. I&#8217;m happy to say this is all being done as we speak (thanks kuja!), so very soon you&#8217;ll find an even better approach to Doctrine integration.</p><p><span
id="more-137"></span></p><h2>Other Lithium Tutorials</h2><p>Of course I&#8217;m not the only one to have written about Lithium! Here are some tutorials you may enjoy:</p><ul><li>Video Tutorial: <a
href="http://vimeo.com/8005503">li3 on Apache2</a>, and <a
href="http://vimeo.com/8037408">li3 on IIS7</a>.</li><li>The original <a
href="http://rad-dev.org/lithium/wiki/drafts/blog-tutorial">Lithium blog tutorial</a>.</li></ul><h2>The Basics</h2><p>For the sakes of me being lazy, I&#8217;ll assume you have prior knowledge of:</p><ul><li>PHP 5.3 basics</li><li>Another PHP framework (like <a
href="http://www.cakephp.org">CakePHP, the most popular PHP framework</a>).</li><li>Using GIT (you should have it installed, Ubunty folks: <code>$ sudo apt-get install git-core</code>)</li><li>You should have an account setup with your SSH key in <a
href="http://rad-dev.org/users/add">rad-dev.org</a> (<a
href="http://rad-dev.org/lithium/wiki/guides/installation">instructions here</a>)</li></ul><h3>Adding PHP 5.3 support</h3><p>There are many ways people have shown how to enable PHP 5.3 support on your servers. In my case, since I run my own <a
href="/ubuntu/apache2-php5-mysql-xdebug-ssl-svn-python-trac-on-ubuntu-hardy-8-04-64-bits/">compiled version of Apache + PHP</a>, it gets easier. I&#8217;ll show you here how to set up dual support (PHP 5.2 and PHP 5.3) on your Apache in case you are as smart as me, and decided to build it yourself as well ;-]</p><p>First download and compile PHP 5.3 (this instructions point to the latest release of PHP 5.3 at the time of this writing, you may need to check <a
href="http://www.php.net">PHP.net</a> to see if there&#8217;s a newer version):</p><pre name="code">
$ cd /usr/local/src
$ sudo wget http://ar.php.net/get/php-5.3.1.tar.gz/from/this/mirror
$ sudo tar xzvf php-5.3.1.tar.gz
$ sudo chown -R $USER:$USER php-5.3.1/
$ cd php-5.3.1/
$ ./configure \
  --prefix=/usr/local/php5.3 \
  --with-config-file-path=/usr/local/php5.3 \
  --with-libxml-dir=/usr/include/libxml2 \
  --with-mysql=/usr/local/mysql \
  --enable-pdo \
  --with-pdo-mysql=/usr/local/mysql \
  --with-zlib=/usr/local/zlib \
  --with-curl \
  --with-gd \
  --with-jpeg-dir=/usr/lib \
  --with-png-dir=/usr/lib \
  --with-freetype-dir=/usr/lib \
  --with-gettext \
  --enable-mbstring \
  --enable-soap \
  --enable-ftp \
  --with-openssl
$ make
$ sudo make install
</pre><p>Easy right? Now, whenever you want to add a virtual host that supports PHP 5.3, you&#8217;ll have to add these lines inside the &lt;VirtualHost&gt; directive:</p><pre name="code">
ScriptAlias /cgi-bin/ /usr/local/php5.3/bin/
Action php53-cgi /cgi-bin/php-cgi
AddHandler php53-cgi .php5 .php

&lt;Directory "/usr/local/php5.3/bin"&gt;
	Options +ExecCGI -Includes
	Order allow,deny
	Allow from all
&lt;/Directory&gt;
</pre><h3>Installing Lithium</h3><p>First thing you&#8217;ll have to do is clone from lithium&#8217;s git repository. We&#8217;ll do it in a generic place, that we&#8217;ll be able to share from all our lithium applications, in order to keep up with the amazing ongoing development in Lithium. So let&#8217;s clone that stuff:</p><pre name="code">
$ cd /var/www
$ git clone code@rad-dev.org:lithium.git lithium
</pre><p>So now in our <code>/var/www/lithium</code> directory we have lithium installed. Every time we&#8217;ll want to ugrade to lithium&#8217;s latest release, we&#8217;ll just do:</p><pre name="code">
$ cd /var/www/lithium
$ git pull --rebase
</pre><h3>Setup the application structure</h3><p>We now have a generic place (<code>/var/www/lithium</code>) where we have lithium installed. Each time we start a new application, we&#8217;ll just copy over the application directory, and link to lithium&#8217;s core directory.</p><p>So we want to create an application in the host <code>http://blog.li3</code> so we&#8217;ll store its files in <code>/var/www/blog.li3</code>. Let&#8217;s copy the files, and link to the core:</p><pre name="code">
$ mkdir /var/www/blog.li3
$ cd /var/www/blog.li3
$ cp -pR /var/www/lithium/app .
$ ln -s /var/www/lithium/libraries libraries
</pre><h3>Add the virtual host</h3><p>Ok we now have the application installed in <code>/var/www/blog.li3</code>. Let&#8217;s create the virtual host for it. First edit your <code>/etc/hosts</code> file and add this line:</p><pre name="code">
127.0.1.1	blog.li3
</pre><p>Now let&#8217;s add the virtual host to apache. Edit the file <code>/usr/local/apache2/conf/extra/httpd-vhosts.conf</code> (obviously this may be different in your setup) and add the following:</p><pre name="code">
&lt;VirtualHost *:80&gt;
	ServerName blog.li3
	DocumentRoot "/var/www/blog.li3/app/webroot"
	ErrorLog "logs/blog.li3.error.log"
	CustomLog "logs/blog.li3.access.log" combined

	ScriptAlias /cgi-bin/ /usr/local/php5.3/bin/
	Action php53-cgi /cgi-bin/php-cgi
	AddHandler php53-cgi .php5 .php

	&lt;Directory "/usr/local/php5.3/bin"&gt;
		Options +ExecCGI -Includes
		Order allow,deny
		Allow from all
	&lt;/Directory&gt;
&lt;/VirtualHost&gt;
</pre><p>You should now restart apache:</p><pre name="code">
$ sudo /etc/init.d/apachectl restart
</pre><p>And you should be able to browse to <a
href="http://blog.li3">http://blog.li3</a> and see a screen just like this:</p><div
id="attachment_142" class="wp-caption aligncenter" style="width: 310px"><a
href="http://marianoiglesias.com.ar/wp-content/uploads/2010/01/blog_li3-screen1.png"><img
src="http://marianoiglesias.com.ar/wp-content/uploads/2010/01/blog_li3-screen1-300x183.png" alt="" title="blog_li3-screen1" width="300" height="183" class="size-medium wp-image-142" /></a><p
class="wp-caption-text">Lithium's welcome screen</p></div><h3>Install Doctrine 2.0</h3><p>We&#8217;ll install doctrine as a system-wide library. Therefore we&#8217;ll move to our generic lithium install&#8217;s library path (<code>/var/www/lithium/libraries</code>) and download doctrine inside. Now we need the Doctrine ORM release (you may need to check the <a
href="http://www.doctrine-project.org/download">doctrine download page</a> for a more up-to-date version of the 2.0 branch:</p><pre name="code">
$ cd /var/www/lithium/libraries
$ mkdir doctrine
$ cd doctrine
$ wget http://www.doctrine-project.org/download/2_0_0_ALPHA3/format/tgz/package/ORM
$ tar -xzvf DoctrineORM-2.0.0-ALPHA3.tgz
</pre><h2>Loading the Database</h2><p>Now that we have the basics in place (PHP 5.3 enabled, Lithium installed, Doctrine installed), we&#8217;ll start building our application.</p><h3>Creating the Database and loading some data</h3><p>Let&#8217;s start by creating a database named <code>blog_li3</code>:</p><pre name="code">
$ mysqladmin -uroot -p create blog_li3
</pre><p>Now load this SQL statements to the database we&#8217;ve just created:</p><pre name="code" class="sql">
CREATE TABLE `posts`(
    `id` INT NOT NULL AUTO_INCREMENT,
    `title` VARCHAR(255) NOT NULL,
    `body` TEXT NOT NULL,
    `published` BOOLEAN NOT NULL default FALSE,
    `created` DATETIME NOT NULL,
    `modified` DATETIME NOT NULL,
    PRIMARY KEY(`id`)
);

CREATE TABLE `comments`(
    `id` INT NOT NULL AUTO_INCREMENT,
    `post_id` INT NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `email` VARCHAR(255) NOT NULL,
    `body` TEXT NOT NULL,
    `created` DATETIME NOT NULL,
    `modified` DATETIME NOT NULL,
    PRIMARY KEY(`id`)
);

ALTER TABLE `comments`
    ADD KEY `post_id`(`post_id`),
    ADD CONSTRAINT `comments__posts` FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`);

INSERT INTO `posts` VALUES
    (1, 'First post', 'This is the body for the first post', TRUE, NOW(), NOW());

INSERT INTO `comments` VALUES
    (1, 1, 'Mariano Iglesias', 'mariano@email.com', 'This is a nice post!', NOW(), NOW());
</pre><h3>Setting up the DB connection</h3><p>Let&#8217;s add the basics Doctrine needs to run into Lithium&#8217;s bootstrap. Edit the file <code>app/config/bootstrap.php</code> in your application directory (<code>/var/www/blog.li3</code>) and add the following at the end:</p><pre name="code" class="php">
/**
 * Load Doctrine
 */

Libraries::add('Doctrine', array(
	'path' => LITHIUM_LIBRARY_PATH . '/doctrine/DoctrineORM-2.0.0/Doctrine'
));
</pre><p>Now how easy was that? Since Doctrine shares the same name structure Lithium does, the inclusion of Doctrine classes in Lithium is that easy. Next step: let&#8217;s set up the connection. Open <code>app/config/connections.php</code> and replace the whole <code>Connections::add</code> expression with the following (notice how we are adding a connection of type &#8216;doctrine&#8217; rather than the default type &#8216;database&#8217; lithium provides):</p><pre name="code" class="php">
Connections::add('default', 'doctrine', array(
	'driver' => 'pdo_mysql',
	'host' => 'localhost',
	'user' => 'root',
	'password' => 'password',
	'dbname' => 'blog_li3'
));
</pre><h3>Creating a wrapper for some Doctrine functionality</h3><p>If you go through <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en">doctrine documentation</a>, you&#8217;ll see it&#8217;s quite powerful and flexible. However, we&#8217;ll want to have a basic wrapper around the most common functionality (establishing the connection, querying a model, creating / editing / deleting records) so our work from our controllers is more transparent, and Doctrine agnostic. So let&#8217;s create a base class for all models (CakePHP folks, this will sound familiar) and name it <code>AppModel</code></p><p>First, the code. Create a file named <code>AppModel.php</code> in your <code>app/</code> directory, with the following contents.</p><pre name="code" class="php">
&lt;?php
namespace app;

use \lithium\data\Connections;

abstract class AppModel extends \lithium\data\model\Record {
	protected static $_alias;
	protected static $_entityManagers = array();

	public static function create($data = array()) {
		$class = get_called_class();
		$record = new $class();
		return $record-&gt;set($data);
	}

	public function set($data = array()){
		if (isset($data) &#038;&#038; count($data) &gt; 1) {
			if (!isset($data['created']) &#038;&#038; !isset($data['id']) &#038;&#038; !isset($this-&gt;id)) {
				$data['created'] = new \DateTime(date('Y-m-d H:i:s'));
			}

			if (!isset($data['modified'])) {
				$data['modified'] = new \DateTime(date('Y-m-d H:i:s'));
			}

			foreach($data as $key =&gt; $value) {
				$this-&gt;$key = $value;
			}
		}
		return $this;
	}

	public function save() {
		$entityManager = $this-&gt;_entityManager();
		$entityManager-&gt;persist($this);
		$entityManager-&gt;flush();
		return $this;
	}

	public function delete($id = null) {
		if (empty($id)) {
			$id = $this-&gt;id;
		}
		$entityManager = $this-&gt;_entityManager();
		$entityManager-&gt;remove($this);
		$entityManager-&gt;flush();
		return $this;
	}

	public static function find($type = 'first', $query = array()) {
		$class = get_called_class();
		$alias = self::$_alias ?: substr($class, strrpos($class, '\\') + 1);
		$methods = array('first', 'all');

		if (!in_array($type, $methods)) {
			throw new \Exception("Find method \"{$type}\" is not implemented");
		}

		$query = array_merge(array(
			'fields' =&gt; null,
			'conditions' =&gt; null,
			'order' =&gt; null
		), $query);

		$fields = array();
		if (isset($query['fields'])) {
			foreach($query['fields'] as $field) {
				$model = $alias;
				if (strpos($field, '.') === false) {
					list($model, $field) = explode('.', $field);
				}
			}
		} else {
			$fields[] = $alias;
		}

		$queryBuilder = self::_entityManager()-&gt;createQueryBuilder();
		$queryBuilder-&gt;add('select', $alias)-&gt;add('from', $queryBuilder-&gt;expr()-&gt;from($class, $alias));

		$expression = self::_conditionsToDqlExpr($alias, $queryBuilder, $query['conditions']);
		if (isset($expression)) {
			$queryBuilder-&gt;add('where', $expression);
		}

		if (!empty($query['order'])) {
			$order = $query['order'];
			if (is_array($query['order'])) {
				$order = array();
				foreach($query['order'] as $field =&gt; $direction) {
					if (!is_string($field)) {
						$field = $direction;
						$direction = 'asc';
					}
					$order[] = (strpos($field, '.') === false ? $alias . '.' : '') . $field . ' ' . $direction;
				}
				$order = implode(',', $order);
			}
			$queryBuilder-&gt;add('orderBy', $order);
		}

		$dql = $queryBuilder-&gt;getDql();
		$result = self::_entityManager()-&gt;createQuery($dql)-&gt;getResult();
		if (isset($result) &#038;&#038; $type == 'first') {
			$result = current($result);
		}
		return $result;
	}

	protected static function _conditionsToDqlExpr($alias, $queryBuilder, $conditions) {
		$expr = $queryBuilder-&gt;expr();
		if (empty($conditions)) {
			return null;
		} else if (is_string($conditions)) {
			$expr = $expr-&gt;andx($conditions);
		} else {
			foreach($conditions as $key =&gt; $value) {
				if (is_string($key) &#038;&#038; in_array(strtolower($key), array('and', 'or'))) {
					$clause = strtolower($key);
					$pieces = array();
					foreach((array) $value as $innerKey =&gt; $piece) {
						if (is_string($innerKey)) {
							$piece = array($innerKey =&gt; $piece);
						}
						$pieces[] = self::_conditionsToDqlExpr($alias, $queryBuilder, $piece);
					}
					$expr = call_user_func_array(array($expr, $clause . 'x'), $pieces);
				} else if (is_string($key)) {
					if (strpos($key, '.') === false) {
						$key = $alias . '.' . $key;
					}
					if (is_array($value)) {
						$values = $value;
						foreach($values as $iv =&gt; $value) {
							$values[$iv] = $expr-&gt;literal($value);
						}
						$expr = $expr-&gt;in($key, $values);
					} else {
						$expr = $expr-&gt;eq($key, $expr-&gt;literal($value));
					}
				} else {
					$expr = $expr-&gt;andx(self::_conditionsToDqlExpr($alias, $queryBuilder, $value));
				}
			}
		}

		return $expr;
	}

	protected static function _entityManager($connectionName = 'default') {
		if (!isset(self::$_entityManagers[$connectionName])) {
			$connection = Connections::get($connectionName, array('config'=&gt;true));
			if (!isset($connection)) {
				throw new \Exception("Configuration {$connectionName} not found");
			} else if ($connection['type'] != 'doctrine') {
				throw new \Exception("Configuration {$connectionName} is not a doctrine configuration");
			}

			$config = new \Doctrine\ORM\Configuration();
			$config-&gt;setProxyDir(LITHIUM_APP_PATH . '/models');
			$config-&gt;setProxyNamespace('app\models');

			self::$_entityManagers[$connectionName] = \Doctrine\ORM\EntityManager::create($connection, $config);
		}
		return self::$_entityManagers[$connectionName];
	}

	/**
	 * Overrides for lithium/data/model/Record
	 */

	public function exists() {
		return !empty($this-&gt;id);
	}

	public function __get($name) {
		return isset($this-&gt;$name) ? $this-&gt;$name : null;
	}
}

?&gt;
</pre></p><p>That&#8217;s a lot, right? First of all, this is generic enough so we won&#8217;t go through the detailed explanation line by line, but we&#8217;ll go through what each method does, and why is there. So let&#8217;s see:</p><ul><li><code>create()</code>: creates a new instance of a record class, loading it with the specified data.</li><li><code>set()</code>: sets the specified data on the current record instance</li><li><code>save()</code>: saves (persists) the current record</li><li><code>delete()</code>: deletes the current record, or the record whose ID is specified</li><li><code>find()</code>: it finds :-] It tries to mimic the same parameters CakePHP has on its find method. Takes two parameters: first, the find type (only &#8216;first&#8217; and &#8216;all&#8217; supported here), and second an array of options (supported options: &#8216;fields&#8217;, &#8216;conditions&#8217;, &#8216;order&#8217;)</li><li><code>_conditionsToDqlExpr()</code>: helper method used by find() to convert the array based conditions into a Doctrine DQL Query (using <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en/query-builder">Doctrine&#8217;s QueryBuilder</a>)</li><li><code>_entityManager()</code>: gives back <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en/working-with-objects">Doctrine&#8217;s EntityManager</a> used to perform actual operations</li><li><code>exists()</code>: Tells if current record exists. Overriden from Lithium&#8217;s record to add support for the record in the FormHelper</li><li><code>__get()</code>: Gives back the given field&#8217;s value for current record. Overriden from Lithium&#8217;s record to add support for the record in the FormHelper</li></ul><h2>The models</h2><p>Ok now to the fun part, creating the models our application will need. If you remember, the SQL script created two tables: <code>posts</code> and <code>comments</code>. Therefore we&#8217;ll have two models, <code>Post</code> and <code>Comment</code>. In order to link each comment&#8217;s property to a real table field, we&#8217;ll use <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en/basic-mapping">Doctrine&#8217;s annotations</a>. They define the characteristics of each record field through source code comments.</p><p>Let&#8217;s first create the <code>Post</code> model. Create a file named <code>Post.php</code> in your <code>app/models</code> folder with the following contents:</p><pre name="code" class="php">
&lt;?php
namespace app\models;

/**
 * @Entity
 * @Table(name="posts")
 */
class Post extends \app\AppModel {
	/**
	 * @Id @Column(type="integer")
	 * @GeneratedValue(strategy="AUTO")
	 */
	public $id;

	/** @Column(type="string") */
	public $title;

	/** @Column(type="text") */
	public $body;

	/** @Column(type="datetime") */
	public $created;

	/** @Column(type="datetime") */
	public $modified;

	/** @OneToMany(targetEntity="Comment", mappedBy="post", cascade={"remove"}) */
	public $comments;
}
?&gt;
</pre><p>To understand the actual annotations, go through Doctrine&#8217;s introduction to <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en/basic-mapping">docblock annotations</a>. What it&#8217;s important for us is seeing that we have a property for each field in our <code>posts</code> table (what we want mapped to our record class), plus a property to store all related comments (the <code>$comments</code> property), each of which will be an instance of the <code>Comment</code> class.</p><p>So what about the <code>Comment</code> class?  Create a file named <code>Comment.php</code> in your <code>app/models</code> folder with the following contents:</p><pre name="code" class="php">
&lt;?php
namespace app\models;

/**
 * @Entity
 * @Table(name="comments")
 */
class Comment extends \app\AppModel {
	/**
	 * @Id @Column(type="integer")
	 * @GeneratedValue(strategy="AUTO")
	 */
	public $id;

	/** @Column(type="string") */
	public $name;

	/** @Column(type="string") */
	public $email;

	/** @Column(type="text") */
	public $body;

	/** @Column(type="datetime") */
	public $created;

	/** @Column(type="datetime") */
	public $modified;

	/**
	 * @ManyToOne(targetEntity="Post")
	 * @JoinColumn(name="post_id", referencedColumnName="id")
	 */
	public $post;
}
?&gt;
</pre><p>Just as before, we have a property per field, plus a property named <code>$post</code> to hold the instance of the <code>Post</code> a <code>Comment</code> belongs to.</p><h2>The Controllers</h2><p>Let&#8217;s first create the basic controller for <code>Post</code> records, that will simply show the list of post titles, with links for operations that we don&#8217;t yet have (editing, adding, removing, etc.) Create a file named <code>PostsController.php</code> in your <code>app/controllers</code> folder with the following contents.</p><pre name="code" class="php">
&lt;?php
namespace app\controllers;

use app\models\Post;

class PostsController extends \lithium\action\Controller {
	public function index() {
		$posts = Post::find('all');
		$this-&gt;set(compact('posts'));
	}
}
?&gt;
</pre><p>That&#8217;s a very simple controller, isn&#8217;t it? We just get the posts, and pass it to the view as a <code>$posts</code> variable. So let&#8217;s create the view then. Create a folder named <code>posts</code> in your <code>app/views/</code> folder, and then create a file named <code>index.html.php</code> in the newly created <code>app/views/posts</code> folder with the following contents:</p><pre name="code" class="php">
&lt;h1&gt;Posts&lt;/h1&gt;
&lt;p&gt;&lt;?php echo $this-&gt;html-&gt;link('Add new Post', '/posts/edit'); ?&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;?php foreach($posts as $post) { ?&gt;
	&lt;li&gt;
		&lt;?php echo $this-&gt;html-&gt;link($post-&gt;title, '/posts/view/' . $post-&gt;id); ?&gt;
		[&lt;?php echo $this-&gt;html-&gt;link('Edit', '/posts/edit/' . $post-&gt;id); ?&gt;]
		[&lt;?php echo $this-&gt;html-&gt;link('Delete', '/posts/delete/' . $post-&gt;id, array('onClick'=&gt;'return confirm("Are you sure you want to delete this record");')); ?&gt;]
	&lt;/li&gt;
&lt;?php } ?&gt;
&lt;/ul&gt;
</pre><p>This code also speaks for itself. So let&#8217;s now create an action to view an actual post. Add a method named <code>view()</code> in your <code>PostsController</code> with the following code:</p><pre name="code" class="php">
public function view($id = null) {
	$id = $id ?: $this-&gt;request-&gt;id;
	$post = Post::find('first', array('conditions' =&gt; compact('id')));
	$this->set(compact('post'));
}
</pre><p>We are basically getting the ID first (since it&#8217;s a numeric ID, a convenient Lithium route check will set it as part of the request information), and then looking for a <code>Post</code> with the given ID. That post will also contain the comments attached to it, so knowing that we can create the view for this action. Create a file named <code>view.html.php</code> in your <code>app/views/posts</code> folder with the following contents:</p><pre name="code" class="php">
&lt;h1&gt;&lt;?php echo $post-&gt;title; ?&gt;&lt;/h1&gt;
&lt;p&gt;&lt;small&gt;&lt;?php echo $post-&gt;created-&gt;format('F d, Y H:i'); ?&gt;&lt;/small&gt;&lt;/p&gt;
&lt;?php echo $post-&gt;body; ?&gt;

&lt;?php if (!$post-&gt;comments-&gt;isEmpty()) { ?&gt;
	&lt;h3&gt;Comments&lt;/h3&gt;
	&lt;?php foreach($post-&gt;comments as $comment) { ?&gt;
			&lt;p&gt;&lt;small&gt;
			by &lt;a href="mailto:&lt;?php echo $comment-&gt;email; ?&gt;"&gt;&lt;?php echo $comment-&gt;name; ?&gt;&lt;/a&gt;
			on &lt;?php echo $comment-&gt;created-&gt;format('F d, Y H:i'); ?&gt;
			&lt;/small&gt;&lt;/p&gt;
			&lt;?php echo $comment-&gt;body; ?&gt;
	&lt;?php } ?&gt;
&lt;?php } ?&gt;

&lt;h3&gt;Add your Comment&lt;/h3&gt;
&lt;?php echo $this-&gt;form-&gt;create(null, array('url'=&gt;array('controller'=&gt;'comments', 'action'=&gt;'add'))); ?&gt;
	&lt;?php echo $this-&gt;form-&gt;hidden('post_id', array('value' =&gt; $post-&gt;id)); ?&gt;
	&lt;label&gt;Your email:&lt;/label&gt; &lt;?php echo $this-&gt;form-&gt;text('email', array('size'=&gt;60)); ?&gt;
	&lt;label&gt;Your name:&lt;/label&gt; &lt;?php echo $this-&gt;form-&gt;text('name', array('size'=&gt;60)); ?&gt;
	&lt;label&gt;Comment:&lt;/label&gt; &lt;?php echo $this-&gt;form-&gt;textarea('body', array('rows'=&gt;5, 'cols'=&gt;60)); ?&gt;
	&lt;?php echo $this-&gt;form-&gt;submit('Add Comment'); ?&gt;
&lt;?php echo $this-&gt;form-&gt;end(); ?&gt;
</pre><p>This code should also speak for itself, except probably the <code>isEmpty()</code> method, which is part of the collection&#8217;s methods Doctrine provides.</p><p>Another thing to note in the above view is the form at the bottom, used to add comments to a post. First thing we notice there is that we are setting as action for the post an action called <code>add</code> in a controller named <code>Comments</code>. We don&#8217;t yet have this controller, do we? So let&#8217;s add it.</p><p>Create a file named <code>CommentsController.php</code> in your <code>app/controllers</code> folder with the following contents:</p><pre name="code" class="php">
&lt;?php
namespace app\controllers;

use app\models\Post;
use app\models\Comment;

class CommentsController extends \lithium\action\Controller {
	public function add() {
		if (empty($this-&gt;request-&gt;data)) {
			$this-&gt;redirect('/');
		}

		$post = Post::find('first', array(
			'conditions'=&gt;array('Post.id' =&gt; $this-&gt;request-&gt;data['post_id'])
		));
		if (!$post) {
			throw new \Exception("No post found for ID {$this-&gt;request-&gt;data['post_id']}");
		}

		$comment = Comment::create($this-&gt;request-&gt;data);
		$comment-&gt;post = $post;
		$comment-&gt;save();

		$this-&gt;redirect('/posts/view/' . $post-&gt;id);
	}
}
?&gt;
</pre><p>This action looks more involved. First thing we do, is make sure someone is posting data to this action (that&#8217;s the check on <code>$this-&gt;request-&gt;data</code>, where the posted data comes in Lithium). If they don&#8217;t, we kick their butts. Next, we make sure there&#8217;s an actual post for the given ID. If there&#8217;s not, we throw an exception. The last part is the actual saving of the comment, so let&#8217;s go through that:</p><ul><li>With <code>$comment = Comment::create($this-&gt;request-&gt;data);</code> we create an instance of the <code>Comment</code> class, initially populating with the posted data (so the properties <code>$email</code>, <code>$name</code>, etc. will get populated with the posted data)</li><li>Next with <code>$comment-&gt;post = $post;</code> we specify to which <code>Post</code> the <code>Comment</code> belongs to.</li><li>Finally we persist (save) the <code>Comment</code> with <code>$comment-&gt;save();</code></li></ul><p>Since we redirect them no matter what, we need no view for this action. Also, you may have noticed that there&#8217;s no validation. That will be left as homework for you. After the comment is saved, we redirect the user back to the post page.</p><p>So we now know how to show the list of posts, how to view a post, and add a comment. All we have to do next is add the ability to add/edit posts, and remove them. Let&#8217;s start with the easy part, deleting a post. Add the following to your <code>PostsController</code>:</p><pre name="code" class="php">
public function delete($id = null) {
	$id = $id ?: $this-&gt;request-&gt;id;
	if (!isset($id)) {
		$this-&gt;redirect('/posts');
	}

	$post = Post::find('first', array('conditions' =&gt; compact('id')));
	if (!$post) {
		throw new \Exception("No post found for ID {$id}");
	}

	$post-&gt;delete();
	$this-&gt;redirect('/posts');
}
</pre><p>This code almost needs no explaining after what we&#8217;ve seen so far. The only important part there is the actual deletion of the <code>Post</code> by calling its <code>delete()</code> method. Also, this is another action that needs no view, since we are redirecting them after the operation.</p><p>Finally, and since we have seen how to add a comment, adding a post won&#8217;t be that different. In fact, we&#8217;ll deal with adding and editing posts in the same action.</p><p>Add the following action to your <code>PostsController</code>:</p><pre name="code" class="php">
public function edit($id = null) {
	$id = $id ?: $this-&gt;request-&gt;id;
	if (isset($id)) {
		$post = Post::find('first', array('conditions' =&gt; compact('id')));
		if (!$post) {
			throw new \Exception("No post found for ID {$id}");
		}
	} else {
		$post = Post::create();
	}

	if (!empty($this-&gt;request-&gt;data)) {
		$post-&gt;set($this-&gt;request-&gt;data)-&gt;save();
		if (!isset($id)) {
			$this-&gt;redirect('/posts');
		}
		$this-&gt;redirect('/posts/view/' . $post-&gt;id);
	}
	$this-&gt;set(compact('post'));
}
</pre><p>The important thing to notice here is that if there&#8217;s an ID provided, we try to get the <code>Post</code>. If there&#8217;s none, we create a new Post record instance by calling <code>Post::create();</code>. Then, once we got submitted data, we simply populate the properties with posted data and save the record, all in a single line: <code>$post-&gt;set($this-&gt;request-&gt;data)-&gt;save();</code>. Yeah, there&#8217;s no validation here either. More homework!</p><p>So what about the view for this action? Create a file named <code>edit.html.php</code> in your <code>app/views/posts</code> folder with the following contents:</p><pre name="code" class="php">
&lt;h3&gt;Edit Post&lt;/h3&gt;
&lt;?php echo $this-&gt;form-&gt;create($post, array('url'=&gt;'/posts/edit/' . $post-&gt;id, 'method'=&gt;'post')); ?&gt;
	&lt;label&gt;Title:&lt;/label&gt; &lt;?php echo $this-&gt;form-&gt;text('title', array('size'=&gt;60)); ?&gt;
	&lt;label&gt;Body:&lt;/label&gt; &lt;?php echo $this-&gt;form-&gt;textarea('body', array('rows'=&gt;5, 'cols'=&gt;60)); ?&gt;
	&lt;?php echo $this-&gt;form-&gt;submit('Add Comment'); ?&gt;
&lt;?php echo $this-&gt;form-&gt;end(); ?&gt;
</pre><p>This is yet another code that speaks for itself. How lazy am I?</code></p><h2>Conclusion</h2><p>We've had fun, didn't we? We now have a very basic blog system working, using PHP 5.3 only tools (<a
href="http://li3.rad-dev.org/">Lithium, the most rad framework</a>, and <a
href="http://www.doctrine-project.org/documentation/manual/2_0/en">Doctrine 2, a very interesting ORM</a>). We could easily extend this further, but we'll wait on the work the Lithium team is doing to offer a much better integration with Doctrine. In the meantime, use this knowledge to have some fun, and build some cool projects!</p><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/cakephp/pagination-with-custom-find-types-in-cakephp/' rel='bookmark' title='Permanent Link: Pagination with custom find types in CakePHP'>Pagination with custom find types in CakePHP</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/li3-lithium/building-a-blog-with-lithium-and-doctrine/feed/</wfw:commentRss> <slash:comments>5</slash:comments> </item> <item><title>Pagination with custom find types in CakePHP</title><link>http://marianoiglesias.com.ar/cakephp/pagination-with-custom-find-types-in-cakephp/</link> <comments>http://marianoiglesias.com.ar/cakephp/pagination-with-custom-find-types-in-cakephp/#comments</comments> <pubDate>Tue, 28 Apr 2009 15:11:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[CakePHP]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/pagination-with-custom-find-types-in-cakephp/</guid> <description><![CDATA[<p>With the release of <a
href="http://cakephp.org">CakePHP 1.2</a> a whole set of new features were made available to us bakers. One of those features is custom find types, which is one of the coolest things that ever happened since <a
href="http://video.google.com/videoplay?docid=-5883772879840922003">I</a>&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/cakephp/modelizing-habtm-join-tables-in-cakephp-1-2-with-and-auto-with-models/' rel='bookmark' title='Permanent Link: Modelizing HABTM join tables in CakePHP 1.2: with and auto-with models'>Modelizing HABTM join tables in CakePHP 1.2: with and auto-with models</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/cakephp-tip-of-the-day-pay-attention-to-conventions/' rel='bookmark' title='Permanent Link: CakePHP tip of the day: pay attention to conventions'>CakePHP tip of the day: pay attention to conventions</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/cakephp-1-2-tip-of-the-day-be-mindful-of-modelset/' rel='bookmark' title='Permanent Link: CakePHP 1.2 tip of the day: be mindful of Model::set'>CakePHP 1.2 tip of the day: be mindful of Model::set</a></li></ol>]]></description> <content:encoded><![CDATA[<p>With the release of <a
href="http://cakephp.org">CakePHP 1.2</a> a whole set of new features were made available to us bakers. One of those features is custom find types, which is one of the coolest things that ever happened since <a
href="http://video.google.com/videoplay?docid=-5883772879840922003">I realized I was cooler than Maverick</a>.</p><p>I&#8217;m not gonna go through custom find types, you can find more info about them <a
href="http://www.pseudocoder.com/archives/2008/10/24/cakephp-custom-find-types/">at Matt&#8217;s blog</a>, or at <a
href="http://c7y.phparch.com/c/entry/1/art,mvc_and_cake">this article</a> written by someone whose name I think I&#8217;ve heard somewhere. What I&#8217;m going to talk about is how to mix your custom find types with pagination, without having to use <a
href="http://book.cakephp.org/view/249/Custom-Query-Pagination">paginate and paginateCount</a> in your models.</p><p>So let&#8217;s first start by building yet another posts table, and inserting some records:</p><pre name="code" class="sql">CREATE TABLE `posts`(
	`id` INT NOT NULL AUTO_INCREMENT,
	`title` VARCHAR(255) NOT NULL,
	`body` TEXT NOT NULL,
	`published` TINYINT(1) NOT NULL default 0,
	`created` DATETIME,
	`modified` DATETIME,
	PRIMARY KEY(`id`)
);

INSERT INTO `posts`(`title`, `body`, `published`, `created`, `modified`) VALUES
	('Post 1', 'Body for Post 1', 1, NOW(), NOW()),
	('Post 2', 'Body for Post 2', 0, NOW(), NOW()),
	('Post 3', 'Body for Post 3', 0, NOW(), NOW()),
	('Post 4', 'Body for Post 4', 1, NOW(), NOW()),
	('Post 5', 'Body for Post 5', 1, NOW(), NOW()),
	('Post 6', 'Body for Post 6', 0, NOW(), NOW()),
	('Post 7', 'Body for Post 7', 1, NOW(), NOW()),
	('Post 8', 'Body for Post 8', 1, NOW(), NOW()),
	('Post 9', 'Body for Post 9', 1, NOW(), NOW());</pre><p>Now let&#8217;s assume we want a find type called <code>published</code> to fetch only the published posts, and that we also want to be able to paginate using this find type. We will be approaching this through a generic approach, something that can be used throughout all our models. With this in mind, let&#8217;s first introduce a model based member variable called <code>$_types</code>, where we define the specific needs of each custom find type. Therefore, that variable will hold what we need as conditions, order, etc. for each custom find type. So let&#8217;s build our <code>Post</code> model:</p><pre name="code" class="php">&lt;?php
class Post extends AppModel {
	public $name = 'Post';
	protected $_types = array(
		'published' =&gt; array(
			'conditions' =&gt; array('Post.published' =&gt; 1),
			'order' =&gt; array('Post.created' =&gt; 'desc')
		)
	);
}
?&gt;</pre><p>As you can see, we define options for each find type as if we would be calling <code>find()</code> directly. So with the above, instead of doing:</p><pre name="code" class="php">$posts = $this-&gt;Post-&gt;find('all', array(
	'conditions' =&gt; array('Post.published' =&gt; 1),
	'order' =&gt; array('Post.created' =&gt; 'desc')
));</pre><p>We can now do:</p><pre name="code" class="php">$posts = $this-&gt;Post-&gt;find('published');</pre><p>Now, what if we wanted to paginate with the above custom find type? Just as we set pagination parameters through the controller member variable <code>$paginate</code>, we can specify which find type pagination we&#8217;ll use. We do so by specifying the find type in the index 0 of the pagination settings. Like so:</p><pre name="code" class="php">$this-&gt;paginate['Post'] = array(
	'published',
	'limit' =&gt; 10
);

$posts = $this-&gt;paginate('Post');</pre><p>Easy, huh? When this is specified, paginate() does the following:</p><ol><li>It issues a <code>find('count')</code> on the Post model, specifying the custom find type (<code>published</code>) in the <code>$options</code> array, through an option named <code>type</code>. Therefore, we can use <code>$options['type']</code> when our model is about to do the count to use the given options for our custom find type.</li><li>It fetches the records by calling <code>find()</code> with the custom find type, <code>find('published')</code> in our example.</li></ol><p>So where&#8217;s that sexy code? Add the following in your <code>AppModel</code>, making the above available for all our models.</p><pre name="code" class="php">&lt;?php
class AppModel extends Model {
	public function find($type, $options = array()) {
		if (!empty($this-&gt;_types)) {
			$types = array_keys($this-&gt;_types);
			$type = (is_string($type) ? $type : null);
			if (!empty($type)) {
				if (($type == 'count' &amp;&amp; !empty($options['type']) &amp;&amp; in_array($options['type'], $types)) || in_array($type, $types)) {
					$options = Set::merge(
						$this-&gt;_types[($type == 'count' ? $options['type'] : $type)],
						array_diff_key($options, array('type'=&gt;true))
					);
				}
				if (in_array($type, $types)) {
					$type = (!empty($this-&gt;_types[$type]['type']) ? $this-&gt;_types[$type]['type'] : 'all');
				}
			}
		}
		return parent::find($type, $options);
	}
}
?&gt;</pre><p>Now ain&#8217;t CakePHP great? Don&#8217;t tell me, <a
href="http://cakefest.org">tell everyone at CakeFest #3</a>.</p><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/cakephp/modelizing-habtm-join-tables-in-cakephp-1-2-with-and-auto-with-models/' rel='bookmark' title='Permanent Link: Modelizing HABTM join tables in CakePHP 1.2: with and auto-with models'>Modelizing HABTM join tables in CakePHP 1.2: with and auto-with models</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/cakephp-tip-of-the-day-pay-attention-to-conventions/' rel='bookmark' title='Permanent Link: CakePHP tip of the day: pay attention to conventions'>CakePHP tip of the day: pay attention to conventions</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/cakephp-1-2-tip-of-the-day-be-mindful-of-modelset/' rel='bookmark' title='Permanent Link: CakePHP 1.2 tip of the day: be mindful of Model::set'>CakePHP 1.2 tip of the day: be mindful of Model::set</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/cakephp/pagination-with-custom-find-types-in-cakephp/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>CakeFest #3: CakePHP in Berlin, July 9-12</title><link>http://marianoiglesias.com.ar/cakephp/cakefest-3-cakephp-in-berlin-july-9-12/</link> <comments>http://marianoiglesias.com.ar/cakephp/cakefest-3-cakephp-in-berlin-july-9-12/#comments</comments> <pubDate>Mon, 23 Mar 2009 16:53:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[CakePHP]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/cakefest-3-cakephp-in-berlin-july-9-12/</guid> <description><![CDATA[<div><p>What better place to talk CakePHP than the world&#8217;s beer nation? That was the question that made the CakePHP Team decide CakeFest third edition should be located in Berlin, Germany. Just as Buenos Aires was the meat fest, I</p></div><p>&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/cakephp/cakephp-1-2-beta-released-and-cakefest-site-launched/' rel='bookmark' title='Permanent Link: CakePHP 1.2 beta released and CakeFest site launched'>CakePHP 1.2 beta released and CakeFest site launched</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/my-talk-in-cakefest-behaviors/' rel='bookmark' title='Permanent Link: My talk in CakeFest: behaviors'>My talk in CakeFest: behaviors</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/cakefest-was-a-success/' rel='bookmark' title='Permanent Link: CakeFest was a success'>CakeFest was a success</a></li></ol>]]></description> <content:encoded><![CDATA[<div><p>What better place to talk CakePHP than the world&#8217;s beer nation? That was the question that made the CakePHP Team decide CakeFest third edition should be located in Berlin, Germany. Just as Buenos Aires was the meat fest, I hereby predict that this will be the Beer Fest.</p><p>Let&#8217;s face it, your code gets better when you use CakePHP. So attending the official CakePHP gathering where all Core Developers and prominent community members go to share their knowledge is an offer you should not ignore. Add awesome beer and guaranteed fun to that recipe, and you would be insane not to join us.</p><p>So what are you waiting for? Go and <a
href="http://cakefest.org/registrations/add">get your CakeFest ticket</a> as soon as possible, you don&#8217;t want to be left behind! If you have a company, or you are a regular Baker just wanting to show your love back to the project, do not hesitate to <a
href="http://cakefest.org/pages/sponsors">sign up for the sponsorship packages</a>. Also help us spread the word by <a
href="http://cakefest.org/pages/badges">placing these CakeFest badges</a> in your site, and you may even get a free ticket!</p></div><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/cakephp/cakephp-1-2-beta-released-and-cakefest-site-launched/' rel='bookmark' title='Permanent Link: CakePHP 1.2 beta released and CakeFest site launched'>CakePHP 1.2 beta released and CakeFest site launched</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/my-talk-in-cakefest-behaviors/' rel='bookmark' title='Permanent Link: My talk in CakeFest: behaviors'>My talk in CakeFest: behaviors</a></li><li><a
href='http://marianoiglesias.com.ar/cakephp/cakefest-was-a-success/' rel='bookmark' title='Permanent Link: CakeFest was a success'>CakeFest was a success</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/cakephp/cakefest-3-cakephp-in-berlin-july-9-12/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Let your MySQL partition breathe</title><link>http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/</link> <comments>http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/#comments</comments> <pubDate>Sat, 28 Feb 2009 22:34:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[PHP]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/let-your-mysql-partition-breathe/</guid> <description><![CDATA[<p> Today I noticed my MySQL partition was taking over 86 GB of the available 120 GB. So I got worried and I wrote this little script to tell me how much space each DB I have is taking:
&#8230;</p>No related posts.]]></description> <content:encoded><![CDATA[<p> Today I noticed my MySQL partition was taking over 86 GB of the available 120 GB. So I got worried and I wrote this little script to tell me how much space each DB I have is taking:</p><pre name="code" class="php">&lt;?php
function format($size) {
	$unit = 'B';
	$units = array(
		'GB' =&gt; 1024 * 1024 * 1024
		, 'MB' =&gt; 1024 * 1024
		, 'KB' =&gt; 1024
	);

	foreach($units as $currentUnit =&gt; $value) {
		if ($size &gt; 2 * $value) {
			$size /= $value;
			$unit = $currentUnit;
			break;
		}
	}

	return number_format($size, 1) . ' ' . $unit;
}

$settings = array(
	'host' =&gt; 'localhost'
	, 'user' =&gt; 'root'
	, 'password' =&gt; 'password'
);

$databases = array();

mysql_connect($settings['host'], $settings['user'], $settings['password']);

$result = mysql_query('show databases');
while($row = mysql_fetch_array($result)) {
	$databases[] = $row['Database'];
}

foreach($databases as $database) {
	$sizes[$database] = 0;

	mysql_select_db($database);
	$result = mysql_query('show table status');
	while($row = mysql_fetch_array($result)) {
		$sizes[$database] += $row['Data_length'] + $row['Index_length'];
	}
}

mysql_close();

foreach($sizes as $database =&gt; $size) {
	echo $database . ' = ' . format($size) . '&lt;br /&gt;';
}

echo '&lt;br /&gt;TOTAL: ' . format(array_sum($sizes));

?&gt;</pre><p> When I ran it, I saw all my DBs where taking a total of 10.8 GB, much less than the 86 GB occupied in the partition. So it hit me, it has to be the binary logs, a set of files that store log events (<a
href="http://dev.mysql.com/doc/refman/5.0/en/binary-log.html">more about them here</a>). Indeed, if you would list the contents of /var/lib/mysql I would find a ton of .bin files, a lot of them as old as from 2007. Therefore, I realized I had to flush the logs.</p><p> In order to flush the binary logs, I logged in to the MySQL console as an administrator, and issued (if you are on a server with replication, you want to <a
href="http://dev.mysql.com/doc/refman/5.0/en/purge-binary-logs.html">purge the binary logs</a> instead):</p><pre name="code" class="sql">FLUSH LOGS;
RESET MASTER;</pre><p>After doing so, the MySQL partition is now taking a total of 12 GB. Much better!</p><p>No related posts.</p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>New fun project: a C++ Web Framework</title><link>http://marianoiglesias.com.ar/c/new-fun-project-a-c-web-framework/</link> <comments>http://marianoiglesias.com.ar/c/new-fun-project-a-c-web-framework/#comments</comments> <pubDate>Sat, 07 Feb 2009 19:37:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[C++]]></category> <category><![CDATA[CLAPP]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/new-fun-project-a-c-web-framework/</guid> <description><![CDATA[<p>As a true programmer, I need fun projects to stay alive. Don&#8217;t get me wrong, my client work fulfills me, and eventhough I&#8217;ve been slacking lately in keeping up with its workload, CakePHP is also a lot of fun. However,&#8230;</p>No related posts.]]></description> <content:encoded><![CDATA[<p>As a true programmer, I need fun projects to stay alive. Don&#8217;t get me wrong, my client work fulfills me, and eventhough I&#8217;ve been slacking lately in keeping up with its workload, CakePHP is also a lot of fun. However, true programmers are always looking for a fun project, one they can call their own. Meet CLAPP, a C++ MVC Web Framework.</p><p>I am not even close to finishing it, but so far I&#8217;m having LOTS of fun. What is amazing is that missing only the M in MVC (the database abstraction layer), CLAPP&#8217;s performance is already astonishing. Running as <a
href="http://www.fastcgi.com">FastCGI</a>, it outperforms the most basic PHP file by a ratio of 1000%. Amazing.</p><p>So what am I looking to gain from this? Nothing, just having fun going back to my absolute favourite language: C++. In the meantime, I get to play with speed comparisons, which gets particularly interesting as I enable more stuff in the framework. My ideal goal is to include a lot of the you-just-have-to-have-this kind of things available on real, serious frameworks such as <a
href="http://www.cakephp.org">CakePHP</a>. This doesn&#8217;t mean that I will hereon start developing every web application in C++, that would just be dumb (if you are asking why, then that&#8217;s because you haven&#8217;t given CakePHP a try). It does mean, however, that I&#8217;ll be posting about CLAPP every now and then.</p><p>To calm your expectations, here&#8217;s a very, VERY small preview of the Dispatcher, which is directly attached to every controller:</p><pre name="code" class="cpp">#ifndef __CLAPP_DISPATCHER_HPP
#define __CLAPP_DISPATCHER_HPP

#include &lt;clapp/cgi_stream.h&gt;
#include &lt;clapp/controller.h&gt;

namespace clapp {
	template &lt;class C&gt;
	class Dispatcher {
		public:
			Dispatcher();
			~Dispatcher();
			void dispatch();

		private:
#ifdef CLAPP_WITH_FASTCGI
			CgiStreamFastCgi * cgiStream;
#else
			CgiStream * cgiStream;
#endif

			void execute();
	};
}

template &lt;class C&gt;
clapp::Dispatcher&lt;C&gt;::Dispatcher() {
#ifdef CLAPP_WITH_FASTCGI
	this-&gt;cgiStream = new CgiStreamFastCgi();
#else
	this-&gt;cgiStream = new CgiStream();
#endif
}

template &lt;class C&gt;
clapp::Dispatcher&lt;C&gt;::~Dispatcher() {
	delete this-&gt;cgiStream;
}

template &lt;class C&gt;
void clapp::Dispatcher&lt;C&gt;::dispatch() {
#ifdef CLAPP_WITH_FASTCGI
	FCGX_Request request;

	FCGX_Init();
	FCGX_InitRequest(&amp;request, 0, 0);

	while (FCGX_Accept_r(&amp;request) == 0) {
		this-&gt;cgiStream-&gt;setRequest(request);
		this-&gt;execute();
		FCGX_Finish_r(&amp;request);
	}
#else
	this-&gt;execute();
#endif
}

template &lt;class C&gt;
void clapp::Dispatcher&lt;C&gt;::execute() {
	C *controller = NULL;

	try {
		controller = new C();

		controller-&gt;setStream(this-&gt;cgiStream);
		controller-&gt;dispatch();

		delete controller;
	} catch(...) {
		if (controller != NULL) {
			delete controller;
		}
		throw;
	}
}

#endif</pre><p>As you can guess from the source code, CLAPP can produce FastCGIs and regular CGIs. It uses <a
href="http://www.clearsilver.net">ClearSilver</a> for its view / layout templates, the <a
href="http://www.fastcgi.com">FastCGI development kit</a> (when FastCGI mode is enabled), and <a
href="http://www.gnu.org/software/cgicc">GNU cgicc</a> as a CGI / FastCGI input wrapper. More news coming soon!</p><p>No related posts.</p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/c/new-fun-project-a-c-web-framework/feed/</wfw:commentRss> <slash:comments>13</slash:comments> </item> <item><title>Seven Things You Don&#8217;t Know About Me (MEME)</title><link>http://marianoiglesias.com.ar/general/seven-things-you-dont-know-about-me-meme/</link> <comments>http://marianoiglesias.com.ar/general/seven-things-you-dont-know-about-me-meme/#comments</comments> <pubDate>Wed, 07 Jan 2009 16:52:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[General]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/seven-things-you-dont-know-about-me-meme/</guid> <description><![CDATA[<div><p>An interesting meme, I was tagged by <a
href="http://jeff.loiselles.com/wordpress/?p=36">Jeff Loiselle</a></p><ol><li>I once worked a full day serving beverages and hamburguers to a lot of people who were attending the agricultural exposition at La Rural. The issue was that</li></ol></div><p>&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/general/argentina-on-the-basketball-world-championship/' rel='bookmark' title='Permanent Link: Argentina on the Basketball World Championship'>Argentina on the Basketball World Championship</a></li><li><a
href='http://marianoiglesias.com.ar/general/argentina-3-71-of-its-population-affected-by-traffic-accidents-every-10-years/' rel='bookmark' title='Permanent Link: Argentina: 3.71 % of its population affected by traffic accidents every 10 years'>Argentina: 3.71 % of its population affected by traffic accidents every 10 years</a></li></ol>]]></description> <content:encoded><![CDATA[<div><p>An interesting meme, I was tagged by <a
href="http://jeff.loiselles.com/wordpress/?p=36">Jeff Loiselle</a></p><ol><li>I once worked a full day serving beverages and hamburguers to a lot of people who were attending the agricultural exposition at La Rural. The issue was that out of every burger / soda we served, me and my friends would get one for ourselves. Not too productive.</li><li>I can&#8217;t sleep without a fan. I&#8217;m not kidding. Even if it&#8217;s freezing, I would turn on the heat and still power on the fan. Just two days ago (I&#8217;m currently on vacations at a beach location south of BA), I had to buy a fan since the house I rent doesn&#8217;t have one.</li><li>I spent almost two years helping out local Rock / Reggae bands, including Todos Tus Muertos &amp; Actitud Maria Marta. I started there because I faxed the manager of AMM telling him if he wanted to build a website. I think this was 1996 or something. Needless to say it was an interesting experience.</li><li>I don&#8217;t know how to drive, nor I can&#8217;t because of an eye condition. In reality, they would give me the license if I wanted to, but I firmly believe I would be a danger to other drivers (even in Argentina, where drivers suck big time.)</li><li>I absolutely hate politicians. With strong passion. I mean, seriously, if you are a politician, get the hell out of my way. I would tap dance on your ass if you don&#8217;t.</li><li>When I was on my last year of high school, I stopped going to class and instead would spend my time at the school bar. I ended up not going for over 7 months to class, so obviously I had to take a test in March for every subject (I think we had over 20), otherwise I wouldn&#8217;t graduate. Since I didn&#8217;t tell my parents what I did, they only thought I had to take just one test, for one class. I had to take them all *while* I was taking the exams to enter University. Somehow it all worked out, and I managed to pass those over 20 exams for high school and 4 university subjects in less than a month. Crazy.</li><li>I absolutely love the new Guns N&#8217; Roses album, Chinese Democracy. I haven&#8217;t bought a music CD since 1993, but I decided to buy this one. Totally worth it. If you don&#8217;t think so, you are clueless about rock.</li></ol><p>Tagees:</p><ul><li><a
href="http://www.clauz.com.ar">Clauz</a>: the coolest person to have near you</li><li><a
href="http://www.debuggable.com">Tim Kosch&#252;tzki</a>: someone who needs help figuring out how to play soccer and not cut his leg in the process</li><li><a
href="http://www.littlehart.net/atthekeyboard/">Chris Hartjes</a>: one of the coolest canucks I&#8217;ve ever met. He gets grumpy though.</li><li><a
href="http://www.mbavio.com.ar">Mart&#237;n Bavio</a>: a fellow baker from argentina, who knows a thing or two about design</li><li><a
href="http://cakedc.com/developer/mark_story">Mark Story</a>: the other awesome canuck, this one is almost the opposite of the grumpy one: he&#8217;s strong with the force, since he never seems to loose his coolness</li><li><a
href="http://www.justitoaca.blogspot.com/">Lorena Iglesias</a>: my fellow sister, a prolific writer (if you don&#8217;t know spanish, she&#8217;s worth the trouble to learn it)</li><li><a
href="http://www.dsh.org/">Dennis Hennen</a>: someone who I&#8217;ve been working with for a long time now, so I happen to know him quite well</li></ul><p>Here are the MEME rules:</p><ul><li>Link your original tagger(s), and list these rules on your blog.</li><li>Share seven facts about yourself in the post &#8212; some random, some weird.</li><li>Tag seven people at the end of your post by leaving their names and the links to their blogs.</li><li>Let them know they&#8217;ve been tagged by leaving a comment on their blogs and/or Twitter.</li></ul></div><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/general/argentina-on-the-basketball-world-championship/' rel='bookmark' title='Permanent Link: Argentina on the Basketball World Championship'>Argentina on the Basketball World Championship</a></li><li><a
href='http://marianoiglesias.com.ar/general/argentina-3-71-of-its-population-affected-by-traffic-accidents-every-10-years/' rel='bookmark' title='Permanent Link: Argentina: 3.71 % of its population affected by traffic accidents every 10 years'>Argentina: 3.71 % of its population affected by traffic accidents every 10 years</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/general/seven-things-you-dont-know-about-me-meme/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Book meme</title><link>http://marianoiglesias.com.ar/ubuntu/book-meme/</link> <comments>http://marianoiglesias.com.ar/ubuntu/book-meme/#comments</comments> <pubDate>Tue, 11 Nov 2008 16:19:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[Ubuntu]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/book-meme/</guid> <description><![CDATA[<div><p> Picking up the meme from <a
href="http://www.jonobacon.org/?p=1378">jono</a> and <a
href="http://matthewhelmke.net/wordpress/2008/11/11/book-meme/">matthew</a>, here&#8217;s my meme:</p><p><cite>&#8220;a state of emergency became the rule&#8221;</cite> &#8211; The Dachau Concentration Camp, 1933 to 1945</p><ol><li>Grab the nearest book.</li><li>Open it to page</li></ol></div><p>&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/general/seven-things-you-dont-know-about-me-meme/' rel='bookmark' title='Permanent Link: Seven Things You Don&#8217;t Know About Me (MEME)'>Seven Things You Don&#8217;t Know About Me (MEME)</a></li></ol>]]></description> <content:encoded><![CDATA[<div><p> Picking up the meme from <a
href="http://www.jonobacon.org/?p=1378">jono</a> and <a
href="http://matthewhelmke.net/wordpress/2008/11/11/book-meme/">matthew</a>, here&#8217;s my meme:</p><p><cite>&#8220;a state of emergency became the rule&#8221;</cite> &#8211; The Dachau Concentration Camp, 1933 to 1945</p><ol><li>Grab the nearest book.</li><li>Open it to page 56.</li><li>Find the fifth sentence.</li><li>Post the text of the sentence in your journal along with these instructions.</li><li>Don&#8217;t dig for your favorite book, the cool book, or the intellectual one: pick the CLOSEST.</li></ol></div><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/general/seven-things-you-dont-know-about-me-meme/' rel='bookmark' title='Permanent Link: Seven Things You Don&#8217;t Know About Me (MEME)'>Seven Things You Don&#8217;t Know About Me (MEME)</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/ubuntu/book-meme/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>MySQL update puts my box to test</title><link>http://marianoiglesias.com.ar/ubuntu/mysql-update-puts-my-box-to-test/</link> <comments>http://marianoiglesias.com.ar/ubuntu/mysql-update-puts-my-box-to-test/#comments</comments> <pubDate>Fri, 29 Aug 2008 21:01:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[Ubuntu]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/mysql-update-puts-my-box-to-test/</guid> <description><![CDATA[<div><p>Wanna test your box performance, both its hard disk and memory? Try setting up a +370 table database, and then build a huge MySQL update file. Run it and get something like this:</p><p
align="center"><a
href="http://cricava.com/blogs/media/el_eternauta/posts/MysqlUpdatePerformance.png"><img
src="http://cricava.com/blogs/media/el_eternauta/posts/MysqlUpdatePerformance-thumb.png" border="0" width="500" height="313" /></a></p></div><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/ubuntu/apache2-php5-mysql-xdebug-ssl-svn-python-trac-on-ubuntu-hardy-8-04-64-bits/' rel='bookmark' title='Permanent Link: Apache2 + PHP5 + MySQL + xDebug + SSL + SVN + Python + Trac on Ubuntu Hardy (8.04) 64 bits'>Apache2 +</a></li></ol>&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/ubuntu/apache2-php5-mysql-xdebug-ssl-svn-python-trac-on-ubuntu-hardy-8-04-64-bits/' rel='bookmark' title='Permanent Link: Apache2 + PHP5 + MySQL + xDebug + SSL + SVN + Python + Trac on Ubuntu Hardy (8.04) 64 bits'>Apache2 + PHP5 + MySQL + xDebug + SSL + SVN + Python + Trac on Ubuntu Hardy (8.04) 64 bits</a></li><li><a
href='http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/' rel='bookmark' title='Permanent Link: Let your MySQL partition breathe'>Let your MySQL partition breathe</a></li></ol>]]></description> <content:encoded><![CDATA[<div><p>Wanna test your box performance, both its hard disk and memory? Try setting up a +370 table database, and then build a huge MySQL update file. Run it and get something like this:</p><p
align="center"><a
href="http://cricava.com/blogs/media/el_eternauta/posts/MysqlUpdatePerformance.png"><img
src="http://cricava.com/blogs/media/el_eternauta/posts/MysqlUpdatePerformance-thumb.png" border="0" width="500" height="313" /></a></p></div><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/ubuntu/apache2-php5-mysql-xdebug-ssl-svn-python-trac-on-ubuntu-hardy-8-04-64-bits/' rel='bookmark' title='Permanent Link: Apache2 + PHP5 + MySQL + xDebug + SSL + SVN + Python + Trac on Ubuntu Hardy (8.04) 64 bits'>Apache2 + PHP5 + MySQL + xDebug + SSL + SVN + Python + Trac on Ubuntu Hardy (8.04) 64 bits</a></li><li><a
href='http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/' rel='bookmark' title='Permanent Link: Let your MySQL partition breathe'>Let your MySQL partition breathe</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/ubuntu/mysql-update-puts-my-box-to-test/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>Firefox is a hungry mother!</title><link>http://marianoiglesias.com.ar/ubuntu/firefox-is-a-hungry-mother/</link> <comments>http://marianoiglesias.com.ar/ubuntu/firefox-is-a-hungry-mother/#comments</comments> <pubDate>Mon, 11 Aug 2008 15:12:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[Ubuntu]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/firefox-is-a-hungry-mother/</guid> <description><![CDATA[<p>Here I&#8217;m working with my humble Dual Processor, 2 GB RAM running Ubuntu 8.04.1, AMD64 edition. I was talking to a colleague / friend of mine and he told me that he (with his 7 GB RAM on a Mac&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/it/well-firefox-behaves-strangely-somehow/' rel='bookmark' title='Permanent Link: Well&#8230; Firefox behaves strangely somehow'>Well&#8230; Firefox behaves strangely somehow</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Here I&#8217;m working with my humble Dual Processor, 2 GB RAM running Ubuntu 8.04.1, AMD64 edition. I was talking to a colleague / friend of mine and he told me that he (with his 7 GB RAM on a Mac OS) has his firefox eating some 750 MB. So I decided to see how much memory was firefox eating on my box. I run this command:</p><pre name="code">ps -C "firefox" -o pid=,vsz=,rss= | awk '{ printf "PID: %d, Virtual: %-.2f MB, Resources: %-.2f MB\n", $1, $2 / 1024, $3 / 1024 }'</pre><p>And I got:</p><pre name="code">PID: 6223, Virtual: 778.06 MB, Resources: 403.70 MB</pre><p>And I had just a couple of tabs open. So I said, ok, let&#8217;s start fresh. Here&#8217;s the same command with a firefox with just an about:blank, having just started up:</p><pre name="code">PID: 9139, Virtual: 462.02 MB, Resources: 56.34 MB</pre><p>Nice to see resources drop, but Virtual is still at over 450 MB. Why oh why are you so hungry, dear firefox?</p><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/it/well-firefox-behaves-strangely-somehow/' rel='bookmark' title='Permanent Link: Well&#8230; Firefox behaves strangely somehow'>Well&#8230; Firefox behaves strangely somehow</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/ubuntu/firefox-is-a-hungry-mother/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>My top 10 commands for July, 2008</title><link>http://marianoiglesias.com.ar/ubuntu/my-top-10-commands-for-july-2008/</link> <comments>http://marianoiglesias.com.ar/ubuntu/my-top-10-commands-for-july-2008/#comments</comments> <pubDate>Wed, 16 Jul 2008 14:30:00 +0000</pubDate> <dc:creator>mariano</dc:creator> <category><![CDATA[Ubuntu]]></category><guid
isPermaLink="false">http://marianoiglesias.com.ar/1/my-top-10-commands-for-july-2008/</guid> <description><![CDATA[<p>Taking inspiration from <a
href="http://www.earobinson.org/2008/04/10/my-top-10-commands-20/">earobinson&#8217;s top commands</a>, I&#8217;ve decided to publish my own list of top 10 commands. Running the following command:</p><pre name="code">history &#124; awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}' &#124; sort -rn &#124; head</pre><p>&#8230;</p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/ubuntu/simple-backup-script-for-ubuntu/' rel='bookmark' title='Permanent Link: Simple backup script for Ubuntu'>Simple backup script for Ubuntu</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Taking inspiration from <a
href="http://www.earobinson.org/2008/04/10/my-top-10-commands-20/">earobinson&#8217;s top commands</a>, I&#8217;ve decided to publish my own list of top 10 commands. Running the following command:</p><pre name="code">history | awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}' | sort -rn | head</pre><p>Brings the following list of top 10 commands:</p><pre name="code">66 ls
40 sudo
37 cd
36 httperf
36 exit
31 svn
18 mysql
17 ssh
17 chmod
15 ./amreg-vpn.sh</pre><p>Only custom script there is amreg-vpn.sh, which looks like the following:</p><pre name="code">if [ "$1" == "" ]
then
	echo "Usage: $0 &lt;start | stop&gt;"
else
	case $1 in
	start)
		echo "Starting VPN..."
		sudo vpnc /etc/vpnc/amreg.conf --natt-mode natt --dpd-idle 0
	;;
	stop)
		echo "Stopping VPN..."
		sudo vpnc-disconnect
	;;
	*)
		echo "Usage: $0 &lt;start | stop&gt;"
	;;
	esac
fi</pre><p>Related posts:<ol><li><a
href='http://marianoiglesias.com.ar/ubuntu/simple-backup-script-for-ubuntu/' rel='bookmark' title='Permanent Link: Simple backup script for Ubuntu'>Simple backup script for Ubuntu</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://marianoiglesias.com.ar/ubuntu/my-top-10-commands-for-july-2008/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
<!-- This site's performance optimized by W3 Total Cache. Dramatically improve the speed and reliability of your blog!

Learn more about our WordPress Plugins: http://www.w3-edge.com/wordpress-plugins/

Minified using disk
Page Caching using disk (user agent is rejected)
Database Caching 73/251 queries in 0.324 seconds using disk

Served from: ps15092.dreamhostps.com @ 2010-03-11 01:25:43 -->