<?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>Mon, 12 Mar 2012 13:25:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Persistent localized routes with Lithium</title>
		<link>http://marianoiglesias.com.ar/php/persistent-localized-routes-with-lithium/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=persistent-localized-routes-with-lithium</link>
		<comments>http://marianoiglesias.com.ar/php/persistent-localized-routes-with-lithium/#comments</comments>
		<pubDate>Sun, 11 Mar 2012 19:33:08 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[Lithium]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=252</guid>
		<description><![CDATA[Just a few minutes ago I got pinged through IRC asking if I could provide a quick answer. The question asked by m99 (thanks for the blog post inspiration!) was if there was an easy way to ensure localized routes with Lithium. As with any challenge you face with lithium, the answer ended up being [...]
Related posts:<ol>
<li><a href='http://marianoiglesias.com.ar/php/route-middleware-with-lithium/' rel='bookmark' title='Route middleware with Lithium'>Route middleware with Lithium</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Just a few minutes ago I got pinged through IRC asking if I could provide a quick answer. The question asked by <strong>m99</strong> (thanks for the blog post inspiration!) was if there was an easy way to ensure localized routes with Lithium. As with any challenge you face with lithium, the answer ended up being dead simple.</p>
<p>So let&#8217;s make sure we understand what we want to do first by enumerating a list of objectives:</p>
<ul>
<li>We want to make sure that our application URLs are prefixed with a locale that identifies the language, and region of choice.</li>
<li>If there is no locale in the URL, we need to figure out what the best choice is for the current visitor, and point them to the same URL they are intending to reach, but with the locale prefix added.</li>
<li>We want to make this prefix persistent (search engine mumble jumble.) So once a locale prefix is set, all links within the application should contain the prefix.</li>
</ul>
<p>Believe it or not, all this is pretty much offered out-of-the-box using nothing but the following Lithium features</p>
<ul>
<li>The awesome <a href="http://lithify.me/docs/manual/handling-http-requests/routing.wiki">continuation route capabilities</a></li>
<li><a href="http://lithify.me/docs/manual/handling-http-requests/routing.wiki">Persistent route parameters</a></li>
<li>The auto-discovery features of Lithium&#8217;s <a href="http://lithify.me/docs/lithium/g11n/Locale">Locale</a> object.</li>
<li>And the superb, out of this world, <a href="http://lithify.me/docs/manual/lithium-basics/filters.wiki">Lithium&#8217;s filtering layer</a>.</li>
</ul>
<p>So let&#8217;s get to work. First thing we want to do is ensure that the <code>g11n.php</code> file is loaded as part of the bootstrap process. So edit your <code>app/config/bootstrap.php</code> file and uncomment the line that requires the <code>app/config/bootstrap/g11n.php</code> file.</p>
<p>Next we want to define which languages are supported in our application. Normally the list of supported languages is defined in the <code>g11n.php</code> file through environment variables, as you can then use that to build up a language menu in your views, or test more languages on different environments. Edit the languages defined in your <code>app/config/bootstrap/g11n.php</code> file. For example my list of languages looks like the following:</p>
<pre class="brush:php">
$locale = 'en_US';
$locales = array(
	'en_US' => 'English',
	'es_AR' => 'Español'
);

Environment::set('production', compact('locale', 'locales'));
Environment::set('development', compact('locale', 'locales'));
Environment::set('test', array('locale' => 'en', 'locales' => array('en' => 'English')));
</pre>
<p>Now we want to make sure we allow locales to be specified as part of an URL. Edit your <code>app/config/routes.php</code> file and add the following route right before any of your custom routes (so it should pretty much stand as the first route defined):</p>
<pre class="brush:php">
Router::connect('/{:locale:[a-z]{2}_[A-Z]{2}}/{:args}', array(), array('continue' => true, 'persist' => array('locale')));
</pre>
<p>We now need to make sure that if there is no locale specified, or if the locale is not valid, we prefix the current action with a valid locale. We also want to be smart and figure out what&#8217;s the best language for the current request, and if there is a language match, but not a region match, we still maintain their desired language (for example, in our <code>g11n.php</code> file we defined <code>es_AR</code> as the only spanish localization, so if someone with an <code>es_ES</code> locale shows up, we still want to take them to a Spanish version of our website.) </p>
<p>Go back to edit your <code>app/config/bootstrap/g11n.php</code> file, and look for a closure bound to a variable named <code>$setLocale</code> right at the very bottom. This <a href="https://github.com/UnionOfRAD/framework/blob/master/app/config/bootstrap/g11n.php#L127">closure</a> normally looks concise, but replace it all with the following:</p>
<blockquote><p><strong>UPDATE</strong>: The best matching code that was taking care of our example <code>es_ES</code> -&gt; <code>es_AR</code> matching scenario has been greatly simplified by the recent pull request merged into Lithium on <a href="https://github.com/UnionOfRAD/lithium/commit/6c95cb80bcf465ffc7695dd6bc387b887a535741">6c95cb80bc</a></p></blockquote>
<pre class="brush:php">
$setLocale = function($self, $params, $chain) {
	$redirect = false;
	$locales = array_keys(Environment::get('locales'));
	if (!$params['request']->locale()) {
		$redirect = true;
		$locale = Locale::preferred($params['request'], $locales);
	} else {
		$locale = $params['request']->locale();
	}

	if (empty($locale) || !in_array($locale, $locales)) {
		$redirect = true;
		if (!empty($locale)) {
			try {
				$locale = Locale::lookup($locales, $locale);
			} catch(\InvalidArgumentException $e) {
				$locale = null;
			}
		} else {
			$locale = null;
		}

		if (empty($locale)) {
			$locale = Environment::get('locale');
		}
	}

	if ($redirect) {
		$params['request']->locale($locale);
		$url = compact('locale') + $params['request']->params;
		return function() use($url) {
			return new \lithium\action\Response(array(
				'location' => $url
			));
		};
	}

	Environment::set(true, array('locale' => $params['request']->locale()));
	return $chain->next($self, $params, $chain);
};
</pre>
<p>Let&#8217;s do a quick summary of what the code above does:</p>
<ul>
<li>Lines <code>02-08</code>: try to get the locale from the current prefix. If no prefix is provided, do it by figuring out what would be the best locale for the current request.</li>
<li>Lines <code>11-26</code>: what to do if there is no locale (something is weird in the request and we can&#8217;t figure out the best locale for the client.), or if the locale specified is not within our list of active locales. The interesting part here are lines <code>13-21</code>, where we do the best-match discovery I mentioned earlier. So if someone is requesting an <code>en_UK</code>  locale, and we only have <code>en_US</code>, we make them use <code>en_US</code>. This block of code ends with a simple fail over: if no locale was found to be best for our client, we use the default locale (lines <code>23-25</code>)</li>
<li>Lines <code>28-36</code>: If we have figured a new locale, we redirect the user to the current URL, but setting the new locale.</li>
</ul>
<p>That&#8217;s it! Try accessing your application with no locale defined. For example if you try to reach <code>http://myapp/signup</code> you will be redirected to <code>http://myapp/en_US/signup</code>, and all your links will be prefixed with the <code>en_US</locale>.</p>
<p>Related posts:<ol>
<li><a href='http://marianoiglesias.com.ar/php/route-middleware-with-lithium/' rel='bookmark' title='Route middleware with Lithium'>Route middleware with Lithium</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/php/persistent-localized-routes-with-lithium/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Responsible buying</title>
		<link>http://marianoiglesias.com.ar/general/responsible-buying/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=responsible-buying</link>
		<comments>http://marianoiglesias.com.ar/general/responsible-buying/#comments</comments>
		<pubDate>Mon, 13 Feb 2012 19:28:54 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=246</guid>
		<description><![CDATA[You may be asking what prompted me to write about anything other than code (disregarding the fact that my last post was written quite a while ago.) Let me tell you. I often debate general issues on my twitter account (sometimes in english, sometimes in my mother tongue, spanish.) Some of these debates end up [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>You may be asking what prompted me to write about anything other than code (disregarding the fact that my last post was written quite a while ago.) Let me tell you. I often debate general issues on my <a href="http://twitter.com/mgiglesias" title="@mgiglesias">twitter account</a> (sometimes in english, sometimes in my mother tongue, spanish.) Some of these debates end up being quite productive, prompting me to further think about the discussion at hand, or to even change some of my behaviors.</p>
<p>This time is the latter. I was, as it is not uncommon in me, tweeting about how <a href="https://twitter.com/#!/mgiglesias/status/162616066386821121">questionable Apple policies</a> are when it comes to the work environment in their factories, when <a href="http://twitter.com/felixge">Felix</a>, a person who I have the strongest respect for, <a href="https://twitter.com/#!/felixge/status/162619710398152704">tweeted something</a> that got my attention:</p>
<blockquote><p>by that logic, you are responsible or every action taken by every company you purchase from</p></blockquote>
<p>My <a href="https://twitter.com/#!/mgiglesias/status/162634107636035584">quick response</a> to him is a thought I wish to ellaborate:</p>
<blockquote><p>and you are. thats the point im making. we should do our best to avoid supporting companies that WE KNOW are being evil</p></blockquote>
<p>If one would boycott all companies that are, at one level or another, doing evil, one would probably have to live alone, in the middle of the desert, consuming nothing but vegetables. I know that&#8217;s not practical. However, we have to draw the line somewhere, right? And don&#8217;t you think that a company that supports, by action or inaction, job environments that are universally accepted as inhuman is somewhere near the line? To give you a bit of background, look at this interview Bill Maher did shortly ago with Mike Daisey (it&#8217;s less than 8 minutes long, you <strong>have the time</strong>):</p>
<p><iframe width="560" height="315" src="http://www.youtube.com/embed/iebnHvxKqlY" frameborder="0" allowfullscreen></iframe></p>
<p>I&#8217;m sure I am backing other companies that may be acting immorally somewhere around the globe, but if I know they are, <strong>I will do my best to avoid giving them business</strong>, and so should you. If I knowingly give them business I am partly responsible for their wrong doings. I know that&#8217;s not something you&#8217;d like to hear, but that&#8217;s a fact.</p>
<p>There will be times where it feels almost impossible to take your business elsewhere. Apple lovers will tell me (and they would mostly be right) that there is no better phone than the iPhone. That the iPad is miles ahead of any other tablet. I know, I get it. They build nice products. But at what cost? Would you rather buy the nice cars that come out of a factory in Nazy germany, or would you like to help Germany (and the rest of the world) free itself from the Nazi horror? I prefer to do the later. Now, please, don&#8217;t panic, I am NOT saying Apple is pro-Nazi (that&#8217;s a comparison that has been largely misused), that&#8217;s just an extreme example to make a point. I am saying that we all know that they are doing something that&#8217;s quite wrong, so we should do our best to stop supporting them.</p>
<p>So even when I always thought I should be a responsible buyer, I am now making an <strong>extra effort</strong>: I will try to inform myself, to the best of my abilities, about what lies behind the product I&#8217;m buying. If it is a sweat shop in Asia, I&#8217;m out. If it&#8217;s cheap because it&#8217;s based on child labor, I&#8217;m out. I am <strong>morally responsible</strong> for my purchases, so I will act accordingly. I may sometimes, inadvertently, support evil companies. If so, I wish to have the capacity to find out about it, and stop supporting those companies.</p>
<p>Let us do this together, shall we?</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/general/responsible-buying/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Route middleware with Lithium</title>
		<link>http://marianoiglesias.com.ar/php/route-middleware-with-lithium/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=route-middleware-with-lithium</link>
		<comments>http://marianoiglesias.com.ar/php/route-middleware-with-lithium/#comments</comments>
		<pubDate>Tue, 03 Jan 2012 16:03:51 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[Lithium]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=232</guid>
		<description><![CDATA[I spend most of my day switching between languages. Sometimes I start the morning with an early dose of LUA, then I get a lot of Python and C++, followed by the yummy dessert that is Node.js. But there&#8217;s always room for PHP. So let&#8217;s talk about PHP today. People often think that when you [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>I spend most of my day switching between languages. Sometimes I start the morning with an early dose of LUA, then I get a lot of Python and C++, followed by the yummy dessert that is Node.js. But there&#8217;s always room for PHP. So let&#8217;s talk about PHP today.</p>
<p>People often think that when you are coding PHP you have to do things &#8220;the PHP way&#8221;. Well, let me clear it up for you: there&#8217;s no such thing as &#8220;the PHP way&#8221;. If there&#8217;s something that defines PHP is its flexibility to be as ugly or as beautiful as you want it to be. With that in mind, what prevents you from taking the lessons learned in one language to another?</p>
<p>That&#8217;s the premise I always have when I&#8217;m coding. Let me give you a for instance. On Node.js, I normally use <a href="http://expressjs.com/">express.js</a> as a framework. Check it out, it&#8217;s pretty awesome. One of the things I love in express.js, is its <a href="http://expressjs.com/guide.html#route-middleware">route middleware</a> capabilities. When I code in any PHP framework, that&#8217;s one of the things I miss the most.</p>
<p>Not many PHP frameworks are flexible enough to support such concepts. One of them is, though. <a href="http://lithify.me">Lithium</a> has many of the things I love about PHP (5.3+ obviously, as I consider anything &lt; 5.3 a waste of my time lately), and some of the things I love about other languages. One of them is Lithium&#8217;s addiction to closures. I love it. They allowed me to take the concept of express.js&#8217; route middleware and apply it to my PHP code.</p>
<p>Let&#8217;s start with a dummy application skeleton. A users table:</p>
<pre class="brush:sql">
CREATE TABLE `users`(
    `id` INT NOT NULL AUTO_INCREMENT,
    `email` VARCHAR(255) NOT NULL,
    `password` VARCHAR(255) NOT NULL,
    PRIMARY KEY(`id`)
);

INSERT INTO `users`(`email`, `password`) VALUES(
    'test@email.com', '$2a$04$U7qYPVYq2YBxqfHL8F2pteERxQYwLTVtAjMIh48Lef9sLSiMVtGHy',
    'john@email.com', '$2a$04$U7qYPVYq2YBxqfHL8F2pteERxQYwLTVtAjMIh48Lef9sLSiMVtGHy'
);
</pre>
<p>In your <code>app/config/bootstrap/connections.php</code>, make sure you uncomment the default database and hook it up to the database owning the table we just created. We&#8217;ll also be using sessions, so uncomment the <code>session.php</code> reference in <code>app/config/bootstrap.php</code>. You may have noticed that our initial users have a password set to a specific value, which means a specific salt was used (the password hashed there in plain text is &#8216;password&#8217;, minus the quotes.). So go ahead and add the following to your <code>app/config/bootstrap/session.php</code> file:</p>
<pre class="brush:php">
use lithium\security\Auth;
use lithium\security\Password;

$salt = '$2a$04$U7qYPVYq2YBxqfHL8F2pte';
Auth::config(array(¬
    'adapter' =&gt; 'Form',
    'model' =&gt; 'Users',
    'filters' =&gt; array('password' => function($text) use($salt){
        return Password::hash($text, $salt);
    }),
    'validators' =&gt; array(
        'password' =&gt; function($form, $data) {
            return (strcmp($form, $data) === 0);
        }
    ),
    'fields' =&gt; array('email', 'password')
));
</pre>
<p>The salt was generated with a call to <code>\lithium\security\Password::salt('bf', 4)</code>. I always use blowfish for password hashing (and 2^16 iterations in production). If you don&#8217;t use blowfish, <a href="http://codahale.com/how-to-safely-store-a-password/">here&#8217;s why you should</a>. Anyway so you may want to store the hash on a better, configurable approach. I opted for a simple variable for this example. Once the salt is defined, I went ahead and configured <code>Auth</code> to use lithium&#8217;s <code>Password::hash()</code> method for hashing using the generated salt, and telling it how to compare hashed passwords against the database value. Pretty simple.</p>
<p>Let&#8217;s now build the <code>Users</code> model. It won&#8217;t have anything in there, really. So just create your <code>app/models/User.php</code> file with the following contents:</p>
<pre class="brush:php">
&lt;?php
namespace app\models;

class Users extends \lithium\data\Model {
}
?&gt;
</pre>
<p>Now the controller. Create a file named <code>app/controllers/UsersController.php</code> with the following contents:</p>
<pre class="brush:php">
&lt;?php
namespace app\controllers;

use lithium\security\Auth;

class UsersController extends \lithium\action\Controller {
    public function login() {
        if (!empty($this-&gt;request-&gt;data)) {
            $user = Auth::check('default', $this-&gt;request);
            if ($user) {
                $this-&gt;redirect(array('action' =&gt; 'view', 'id' =&gt; $user['id']), array('exit' =&gt; true));
            }
        }
    }

    public function logout() {
        Auth::clear('default');
    }
}
?&gt;
</pre>
<p>Nothing really complicated there. Don&#8217;t forget the view in <code>app/views/users/login.html.php</code>:</p>
<pre class="brush:php">
&lt;?php
echo $this-&gt;form-&gt;create();
echo $this-&gt;form-&gt;field('email');
echo $this-&gt;form-&gt;field('password', array('type' =&gt; 'password'));
echo $this-&gt;form-&gt;submit('Login');
echo $this-&gt;form-&gt;end();
?&gt;
</pre>
<p>That should give you a working login / logout. Add some dummy actions to the <code>UsersController.php</code> file:</p>
<pre class="brush:php">
public function view() {
    echo 'view';
    $this-&gt;_stop();
}

public function edit() {
    echo 'edit';
    $this-&gt;_stop();
}
</pre>
<p>Ok now we are ready to play with some route middleware. What we want to achieve is the following:</p>
<ul>
<li>No action named <code>edit</code>, on <strong>any controller</strong>, should be accessible without a logged in user.</li>
<li>When accessing either the <code>Users::edit</code> or <code>Users::view</code> action, there should be an ID specified as a route parameter, and it should match an existing <code>User</code> record.</li>
<li>When accessing the <code>Users::edit</code> action, the given user should match the currently logged in user.</li>
</ul>
<p>These are pretty basic security checks that you would normally put on the controller. Not this time. Edit your <code>app/config/routes.php</code> file and add the following right below the <code>use</code> statements found at the beginning of the file:</p>
<pre class="brush:php">
use lithium\net\http\RoutingException;
use lithium\action\Response;
use lithium\security\Auth;
use app\models\Users;
</pre>
<p>These are all classes that we will use in our route middleware. Let&#8217;s start with the first checkpoint we want to achieve: &#8220;<i>No action named <code>edit</code>, on <strong>any controller</strong>, should be accessible without a logged in user</i>&#8220;. Add the following to the <code>routes.php</code> file, below the content we just added:</p>
<pre class="brush:php">
Router::connect('/{:controller}/{:action:edit}/?.*', array(), array(
    'continue' =&gt; true,
    'handler' =&gt; function($request) {
        if (!Auth::check('default')) {
            return new Response(array('location' =&gt; 'Users::login'));
        }
    }
));
</pre>
<p>The first parameter (<code>continue</code>) ensures that this route definition is treated as a <a href="https://gist.github.com/1512502">continuation route</a>. This is because we don&#8217;t want to interrupt any normal route / parameter processing in this definition. We just wanna &#8220;grab&#8221; all calls to any <code>edit</code> action, and check (using <code>Auth</code>) for a valid user. If none is found, we <strong>process</strong> the request by returning a <code>Response</code>, which in the end redirects the user to the login page. If there is indeed a logged in user, the router will continue looking for other route definitions to match the request. So now all <code>edit</code> actions require a logged in user. Cool.</p>
<p>Next in our list: &#8220;<i>When accessing either the <code>Users::edit</code> or <code>Users::view</code> action, there should be an ID specified as a route parameter, and it should match an existing <code>User</code> record.</i>&#8221; Add the following to the <code>routes.php</code> file, below the content we just added:</p>
<pre class="brush:php">
Router::connect('/{:controller:users}/{:action:edit|view}/{:id:\d*}', array('id' =&gt; null), function($request) {
    if (empty($request-&gt;params['id'])) {
        throw new RoutingException('Missing ID');
    } elseif (!Users::first(array('conditions' =&gt; array('id' =&gt; $request-&gt;params['id'])))) {
        throw new RoutingException('Invalid ID');
    }
});
</pre>
<p>We are now getting more serious. In this definition, we are only matching the <code>Users</code> controller, and actions named either <code>edit</code> or <code>view</code>, which may or may not contain an <code>id</code> parameter. The route handler first checks to make sure the <code>id</code> parameter is given (if not, a <code>RoutingException</code> is thrown.) If the parameter is specified, it is used to find a matching <code>User</code> record with the given ID. If none is found, yet another <code>RoutingException</code> is thrown (you may wish to do something different here, like ensuring a 404 status). If the user is found, the route is not handled, which means some other route definition will handle it (the default route, in this case.)</p>
<p>The final checkpoint we have is: &#8220;<i>When accessing the <code>Users::edit</code> action, the given user should match the currently logged in user.</i>&#8221; So add the following to the <code>routes.php</code> file, below the content we just added:</p>
<pre class="brush:php">
Router::connect('/{:controller:users}/{:action:edit}/{:id:\d+}', array('id' =&gt; null), function($request) {
    $user = Auth::check('default');
    if ($user['id'] != $request-&gt;params['id']) {
        throw new RoutingException('You can only edit your own account');
    }
    return $request;
});
</pre>
<p>This defines a specific match to the <code>Users::edit</code> action with a set <code>id</code> parameter. We use that parameter to make sure it matches the ID of the logged in user. If it doesn&#8217;t match, we throw a <code>RoutingException</code>. If it does match, we return the request as we have successfully processed it.</p>
<p>You can now try accessing the edit and view actions using different scenarios: with a logged in user, while being logged out, editing a user which is not the current logged in user, etc. Everything should be nicely protected. And yet our controller code remained untouched. Nice, huh? That&#8217;s routing middleware for you. <img src='http://marianoiglesias.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/php/route-middleware-with-lithium/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Book release: CakePHP 1.3 Application Development Cookbook</title>
		<link>http://marianoiglesias.com.ar/cakephp/book-release-cakephp-application-development-cookbook/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=book-release-cakephp-application-development-cookbook</link>
		<comments>http://marianoiglesias.com.ar/cakephp/book-release-cakephp-application-development-cookbook/#comments</comments>
		<pubDate>Wed, 30 Mar 2011 22:49:58 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=221</guid>
		<description><![CDATA[Just a few days ago, I was happy to see my first book published. Entitled CakePHP 1.3 Application Development Cookbook, it&#8217;s a book released in the form of a cookbook, with a series of solutions to common problems one faces when developing CakePHP applications. While working on it, I tried to aim for developers at [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>Just a few days ago, I was happy to see my first book published. Entitled <a href="http://www.amazon.com/dp/1849511926/?tag=packtpubli-20">CakePHP 1.3 Application Development Cookbook</a>, it&#8217;s a book released in the form of a cookbook, with a series of solutions to common problems one faces when developing <a href="http://cakephp.org">CakePHP</a> applications.</p>
<p><a href="http://www.amazon.com/dp/1849511926/?tag=packtpubli-20"><img class="alignleft size-medium wp-image-222" style="margin-right: 10px;" title="CakePHP 1.3 Application Development Cookbook cover" src="http://marianoiglesias.com.ar/wp-content/uploads/2011/03/book-medium-243x300.png" alt="" width="194" height="240" /></a>While working on it, I tried to aim for developers at different levels of knowledge, yet a disclaimer has to be made: this is <strong>not a beginners book</strong>. It will not teach you how to install CakePHP, or how to get its friendly URLs working on Microsoft platforms (dodged that bullet.) It is written for CakePHP developers that are looking to solve different problems, and leverage their own applications. So no &#8220;building a blog&#8221; chapter in this book.</p>
<p>There are some recipes that deal with more complex topics, while others deal with what I consider interesting solutions to simple problems. Each recipe starts by proposing a problem, showing the solution, and giving an explanation of how the solution works. Most of the recipes include alternatives and extend the topic at hand beyond the scope of the problem they are solving, and some of them are based on open source packages that CakePHP community members (myself included) released.</p>
<p>This book also benefited from an unbelievably, super-cool, top of the world team of technical reviewers (the <a href="http://mark-story.com">CakePHP 1.3 lead developer</a> happens to be amongst them) that made its code shine (I am known for being humble.) They improved each recipe and proposed awesome alternatives to my original ideas. Because of that, this blog post is being written. I&#8217;m not sure sure I would&#8217;ve been as proud of the original version of the book <img src='http://marianoiglesias.com.ar/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>You should also know that the publishing company behind the publication of this book, <a href="https://www.packtpub.com">Packt Publishing</a>, is donating an important portion of the book earnings to the <a href="http://cakefoundation.org">Cake Software Foundation</a>, which is like <em>The Force</em> behind CakePHP. So I might not get rich, but at least the foundation will get some beers out of each sale. And trust me, nothing says thank you like a beer.</p>
<p>If you bought the book, I welcome any feedback you may have (you could also <a href="http://www.amazon.com/dp/1849511926/?tag=packtpubli-20">leave a review</a> and tell others how super cool the book is.) If you are more of a bytes person and less of a paper person, and look forward to reading the <strong>digital version</strong>, you can also get <a href="http://link.packtpub.com/6ihp7k">the ebook</a>.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/cakephp/book-release-cakephp-application-development-cookbook/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>File uploading with multi part encoding using Twisted</title>
		<link>http://marianoiglesias.com.ar/python/file-uploading-with-multi-part-encoding-using-twisted/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=file-uploading-with-multi-part-encoding-using-twisted</link>
		<comments>http://marianoiglesias.com.ar/python/file-uploading-with-multi-part-encoding-using-twisted/#comments</comments>
		<pubDate>Fri, 20 Aug 2010 03:14:04 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[body]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[deferred]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[multipart]]></category>
		<category><![CDATA[producer]]></category>
		<category><![CDATA[receiver]]></category>
		<category><![CDATA[twisted]]></category>
		<category><![CDATA[upload]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=209</guid>
		<description><![CDATA[If you program in Python and you are building web clients you need to know about Twisted. It is perhaps the most flexible and powerful Python framework for building web clients and servers, and has been proven to work wonderfully under heavy loads. So powerful, that the most basic tasks tend to be a bit [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>If you program in Python and you are building web clients you <strong>need</strong> to know about <a href="http://twistedmatrix.com">Twisted</a>. It is perhaps the most flexible and powerful Python framework for building web clients and servers, and has been proven to work wonderfully under heavy loads. So powerful, that the most basic tasks tend to be a bit of a pain. This is what I realized when I found myself in the need to upload large files to a web site using Twisted. I googled, and I googled, and I found no real answer to my problems.</p>
<p>You can of course find an easy solution: build a multipart request by building the request body yourself out of the files you are trying to upload. Sure, that works. But what happens when you are dealing with a 50 MB file upload? What about 500 MB? I&#8217;m sure you are <strong>not planning</strong> to encode the whole file as a string, right?</p>
<p>This is where Twisted&#8217;s body producers come handy. Implementing your own body producer, you have total control on how to build the request. In fact, Twisted will call you every time it needs data for the request, so you can be sure you won&#8217;t be building the whole chunk in one string. Instead, you will be sending chunks of bytes to what is known as the <em>consumer</em>. What is the consumer? Whatever is asking for a request body.<br />
<span id="more-209"></span><br />
Ok, enough said. I could go on and on with the explanation, but let&#8217;s learn by coding (yay!.) We want to upload a file to a server using Twisted. We also want to know how the file upload is doing (by getting progress callbacks), and when it finishes. Just for the fun of it, we will also send other POST data with the request.</p>
<p>First things first, let us build the server code that will handle the upload. For simplicity (well, that&#8217;s sort of B.S., since Python is simple enough) we&#8217;ll build a PHP script that will take a file upload, and print out the uploaded information, together with any posted data. Here&#8217;s the code for that:</p>
<pre class="brush:php">&lt;?php
$files = array();
foreach ($_FILES as $field => $file) {
	if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
		continue;
	}
	$files[$field] = $file;
}

print_r($_POST);
print_r($files);
?&gt;</pre>
<p>Easy right? We are just printing out whatever we got, discarding any invalid file upload information.</p>
<p>Let&#8217;s look at our client to upload a file, and POST some data to this PHP script. This Python script is using the body producer I built, together with a handy receiver for getting the response. More about that later. Here is the client script:</p>
<pre class="brush:python">from twisted.internet import defer
from twisted.internet import reactor
from twisted.web import client
from twisted.web import http_headers

from pyfire.twistedx import producer
from pyfire.twistedx import receiver

def finished(bytes):
	print "Upload DONE: %d" % bytes

def progress(current, total):
	print "Upload PROGRESS: %d out of %d" % (current, total)

def error(error):
	print "Upload ERROR: %s" % error

def responseDone(data):
	print "Response:"
	print "-" * 80
	print data
	reactor.stop()

def responseError(data):
	print "ERROR with the response. So far I've got:"
	print "-" * 80
	print data
	reactor.stop()

url = "http://kramer/upload.php"
files = {
	"upload": "/home/mariano/myfile.tar.gz"
}
data = {
	"field1": "value1"
}

producerDeferred = defer.Deferred()
producerDeferred.addCallback(finished)
producerDeferred.addErrback(error)

receiverDeferred = defer.Deferred()
receiverDeferred.addCallback(responseDone)
receiverDeferred.addErrback(responseError)

myProducer = producer.MultiPartProducer(files, data, progress, producerDeferred)
myReceiver = receiver.StringReceiver(receiverDeferred)

headers = http_headers.Headers()
headers.addRawHeader("Content-Type", "multipart/form-data; boundary=%s" % myProducer.boundary)

agent = client.Agent(reactor)
request = agent.request("POST", url, headers, myProducer)
request.addCallback(lambda response: response.deliverBody(myReceiver))

reactor.run()</pre>
<p>Let&#8217;s look at what we are doing here. We start by importing some twisted modules we need for this script, and the multipart and receiver modules from the <a href="http://github.com/mariano/pyfire">pyfire module</a> I wrote. Don&#8217;t worry, you don&#8217;t have to use pyfire to use these two modules. Just make sure to download the <a href="http://github.com/mariano/pyfire/tree/master/pyfire/twistedx/">twistedx module</a> that is part of pyfire. Next, we define some callback functions: <code>finished()</code> to inform that the upload is done, <code>progress()</code> to keep us in the loop while the request is being sent to the server, <code>error()</code> in case sh*t happens, and finally <code>responseDone()</code> and <code>responseError()</code> to handle the response from the server, and stop Twisted&#8217;s reactor (its event loop.)</p>
<p>In the actual script code we see that we start by defining the destination URL, and dictionaries with the files to be sent out, and data to post, both of them indexed by field name. We proceed then to start coding the &#8220;Twisted&#8221; way: creating two <em>deferreds</em> that will be used by the producer, and the receiver. If you don&#8217;t know about deferreds you probably haven&#8217;t read the Twisted manual much, so I recommend you go over their <a href="http://twistedmatrix.com/documents/current/core/howto/defer.html">section on deferreds</a>. Basically it&#8217;s a very easy (and flexible, and chainable, and&#8230;) way to get callbacks.</p>
<p>So we build two deferreds: the first one (<code>producerDeferred</code>) is for the producer, where we attach a success callback (the <code>finished()</code> function), and an error callback (the <code>error()</code> function). The second one (<code>receiverDeferred</code>) is used by the receiver, and contains a success callback (the <code>responseDone()</code> function), and an error callback (the <code>responseError()</code> function). Both of these functions will print out whatever data we got as response, and finish by stopping the reactor.</p>
<p>We then build the producer, passing on the files to upload, the data to post, the callback <code>progress()</code> function that will be called throughout the upload, and the deferred for the producer. Similarly, we build the receiver, passing only its deferred.</p>
<p>Having both the producer and the receiver, we can now proceed to create the actual request, not without first creating any additional headers we may need (the request will automatically specify the content length out of the body producer.) Since we are uploading a file, we specify the content type of the request to be <code>multipart/form-data</code>, and as its <code>boundary</code> we set whatever the producer chose as boundary for our chunks in the request body.</p>
<p>The final step is the actual running of the request, doing it the typical Twisted way: first getting an agent for the reactor, creating the request (a POST request to the given URL, with the given headers and body producer), and adding a callback for when we get a response. The callback in this case is a simple <em>lambda</em> function, that delivers the body from the response to the receiver. Finally, we run the reactor.</p>
<p>Notice that when you run the reactor the <code>run()</code> call will block until you stop the reactor. This is why from our response callbacks (<code>responseDone()</code> and <code>responseError()</code>) we stop the reactor whenever we get some sort of response.</p>
<p>If you run this script against your PHP server script, you may get an output like the following:</p>
<pre brush="text">Upload PROGRESS: 153 out of 9120
Upload PROGRESS: 320 out of 9120
Upload PROGRESS: 9080 out of 9120
Upload PROGRESS: 9082 out of 9120
Upload PROGRESS: 9120 out of 9120
Upload DONE: 9120
Response:
--------------------------------------------------------------------------------
Array
(
    [field1] => value1
)
Array
(
    [upload] => Array
        (
            [name] => myfile.tar.gz
            [type] => application/x-tar
            [tmp_name] => /tmp/phplINynd
            [error] => 0
            [size] => 8760
        )
)</pre>
<p>Isn&#8217;t this fun?</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/python/file-uploading-with-multi-part-encoding-using-twisted/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Building a blog with Lithium and Doctrine</title>
		<link>http://marianoiglesias.com.ar/li3-lithium/building-a-blog-with-lithium-and-doctrine/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=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[This post aims to be a very basic introduction to the world of Lithium, also known as li3. As such, it is mostly based on the ubiquitous blog sample. The idea is to learn, through the source code, the basic notions on Lithium and its integration with the Doctrine ORM mapper to build a very [...]
No related posts.]]></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="http://marianoiglesias.com.ar/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 class="brush:bash">$ 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 class="brush:text">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 class="brush:bash">$ 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 class="brush:bash">$ 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 class="brush:bash">$ 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 class="brush:text">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 class="brush:text">&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 class="brush:bash">$ 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 class="size-medium wp-image-142" title="blog_li3-screen1" src="http://marianoiglesias.com.ar/wp-content/uploads/2010/01/blog_li3-screen1-300x183.png" alt="" width="300" height="183" /></a><p class="wp-caption-text">Lithium&#39;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 class="brush:bash">$ 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 class="brush:bash">$ mysqladmin -uroot -p create blog_li3</pre>
<p>Now load this SQL statements to the database we&#8217;ve just created:</p>
<pre class="brush: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 class="brush:php">/**
 * Load Doctrine
 */

Libraries::add('Doctrine', array(
	'path' =&gt; 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 class="brush:php">Connections::add('default', 'doctrine', array(
	'driver' =&gt; 'pdo_mysql',
	'host' =&gt; 'localhost',
	'user' =&gt; 'root',
	'password' =&gt; 'password',
	'dbname' =&gt; '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 class="brush: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) &amp;&amp; count($data) &gt; 1) {
			if (!isset($data['created']) &amp;&amp; !isset($data['id']) &amp;&amp; !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) &amp;&amp; $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) &amp;&amp; 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>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 class="brush: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 class="brush: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 class="brush: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 class="brush: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 class="brush:php">public function view($id = null) {
	$id = $id ?: $this-&gt;request-&gt;id;
	$post = Post::find('first', array('conditions' =&gt; compact('id')));
	$this-&gt;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 class="brush: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 class="brush: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 class="brush: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 class="brush: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 class="brush: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?</p>
<h2>Conclusion</h2>
<p>We&#8217;ve had fun, didn&#8217;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&#8217;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>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/li3-lithium/building-a-blog-with-lithium-and-doctrine/feed/</wfw:commentRss>
		<slash:comments>9</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/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=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[With the release of CakePHP 1.2 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 I realized I was cooler than Maverick. I&#8217;m not gonna go through custom find types, you can find [...]
No related posts.]]></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 class="brush: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 class="brush: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 class="brush: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 class="brush: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 class="brush: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 class="brush: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>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/cakephp/pagination-with-custom-find-types-in-cakephp/feed/</wfw:commentRss>
		<slash:comments>11</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/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=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[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. Let&#8217;s face it, your code gets better [...]
No related posts.]]></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>No related posts.</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/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=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[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: &#60;?php function format($size) { $unit = 'B'; $units = array( 'GB' =&#62; 1024 * 1024 * 1024 [...]
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 class="brush: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 class="brush: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/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=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[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. [...]
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 class="brush: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>16</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using disk: enhanced (User agent is rejected)
Database Caching 29/67 queries in 0.027 seconds using disk: basic
Object Caching 1824/1969 objects using disk: basic

Served from: marianoiglesias.com.ar @ 2012-05-18 03:30:11 -->
