<?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>Cloud Connected &#187; Rémi</title>
	<atom:link href="http://www.cloudconnected.fr/author/rlanvin/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.cloudconnected.fr</link>
	<description>Thoughts of a french web developer</description>
	<lastBuildDate>Wed, 01 Feb 2012 08:53:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Named function parameters in PHP</title>
		<link>http://www.cloudconnected.fr/2012/02/01/named-function-parameters-in-php/</link>
		<comments>http://www.cloudconnected.fr/2012/02/01/named-function-parameters-in-php/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 08:53:57 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=635</guid>
		<description><![CDATA[Officially, PHP doesn&#8217;t support named function parameters. But there is one easy way to emulate this feature &#8211; so easy that it doesn&#8217;t really matter that this feature is missing. It&#8217;s actually a pretty old trick inspired from Javascript I&#8217;ve been using since forever. I thought everyone knew it, but I&#8217;m still surprised by how [...]]]></description>
			<content:encoded><![CDATA[<p>Officially, PHP doesn&#8217;t support named function parameters. But there is one easy way to emulate this feature &#8211; so easy that it doesn&#8217;t really matter that this feature is missing. It&#8217;s actually a pretty old trick inspired from Javascript I&#8217;ve been using since forever. I thought everyone knew it, but I&#8217;m still surprised by how many people come up with overly complicated solutions. Anyway, imagine you have a method that have a lot of optional parameters. Without named parameters, it goes like this:</p>
<pre>
// $queue is required, the rest is optionnal
function bind_queue($queue, $durable = false, $auto_delete = true, $name = false, $answer = null) { ... }
</pre>
<p>Later in your code, if you want to specify another value for <code>$answer</code>, you have to copy the values of every intermediate parameters in your function call. It&#8217;s time consuming, error prone and confusing.</p>
<pre>
// Good luck remember which parameter is what.
bind_queue($my_queue, false, true, false, 42);
</pre>
<p>So instead, it&#8217;s much better use an associative array, like this:</p>
<pre>
// leave the required parameter part of the function definition, the options goes into an array
function bind_queue($queue, array $opt = array())
{
  // we define the default values, and merge with the user values at the same timle
  $opt = array_merge(array(
    'durable' => false,
    'auto_delete' => true,
    'name' => false,
    'answer' => null
  ), $opt);

  // use $opt['answer'] to access the 'answer' optionnal parameter
}
</pre>
<p>Later in your code, if you want to override <code>answer</code>, you can only write this:</p>
<pre>
bind_queue($my_queue, array('answer' => 42));
</pre>
<p>Much easier to read, isn&#8217;t it?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2012/02/01/named-function-parameters-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fixing WordPress auto-update</title>
		<link>http://www.cloudconnected.fr/2012/01/29/fixing-wordpress-auto-update/</link>
		<comments>http://www.cloudconnected.fr/2012/01/29/fixing-wordpress-auto-update/#comments</comments>
		<pubDate>Sun, 29 Jan 2012 13:49:33 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=631</guid>
		<description><![CDATA[Are you tired of WordPress failing to auto-update, and asking for a username for a FTP server even if every possible files and directories have the right permissions (i.e. are writeable by the web server)? Well, me too. Good news is, after countless hours of browsing and trying every possible solution, I stumble upon one [...]]]></description>
			<content:encoded><![CDATA[<p>Are you tired of WordPress failing to auto-update, and asking for a username for a FTP server even if every possible files and directories have the right permissions (i.e. are writeable by the web server)? Well, me too. Good news is, after countless hours of browsing and trying every possible solution, I stumble upon one that works. Just add the following line to <code>wp-config.php</code>:</p>
<pre>define('FS_METHOD', 'direct');</pre>
<p>I tried this out of desperation, and what do you know, the auto-update started to work just fine with this line&#8230; Don&#8217;t get me started on WordPress&#8217; code logic.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2012/01/29/fixing-wordpress-auto-update/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bypassing PHP&#8217;s open_basedir with MySQL</title>
		<link>http://www.cloudconnected.fr/2011/02/25/bypassing-phps-open_basedir-with-mysql/</link>
		<comments>http://www.cloudconnected.fr/2011/02/25/bypassing-phps-open_basedir-with-mysql/#comments</comments>
		<pubDate>Fri, 25 Feb 2011 11:44:07 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=623</guid>
		<description><![CDATA[PHP feature open_basedir is supposed to limit the files that can be opened by PHP to a specified directory-tree (full doc is here). Functions like fopen or file_get_contents will returns an error if the file is outside the allowed directory. So far, it sounds like a good protection. However, it is also very famous for [...]]]></description>
			<content:encoded><![CDATA[<p>PHP feature <code>open_basedir</code> is supposed to limit the files that can be opened by PHP to a specified directory-tree (<a href="http://www.php.net/manual/en/ini.core.php#ini.open-basedir">full doc is here</a>). Functions like <code>fopen</code> or <code>file_get_contents</code> will returns an error if the file is outside the allowed directory. So far, it sounds like a good protection.</p>
<p>However, it is also very famous for being flawed by design and easy to violate (like <code>safe_mode</code> by the way). Well, until now I didn&#8217;t realize how easy it is indeed to bypass it. During a security audit, I add the opportunity to study a backdoor left here by some script kiddie (thanks to an outdated version of a web application). Here is just one interesting example that uses MySQL:</p>
<ol>
<li>create a temporary table</lli>
<li>use MySQL&#8217;s command <code>LOAD DATA INFILE</code> to read any file and load is content to the table</li>
<li>select the content of the table</li>
</ol>
<p>In clean PHP, the code would looks like:</p>
<pre>
$filename = '/etc/passwd';

$pdo = new PDO($dsn, $username, $password);
$pdo->exec('CREATE TEMPORARY TABLE tmp_file ( content LONGBLOB NOT NULL)');
$pdo->exec(sprintf(
	'LOAD DATA INFILE %s INTO TABLE tmp_file',
	$pdo->quote($filename)
));
$content = $pdo->query('SELECT * FROM tmp_file')->fetchAll(PDO::FETCH_COLUMN);
</pre>
<p>To prevent that exploit in particular, it&#8217;s easy: just make sure that the MySQL&#8217;s user doesn&#8217;t have the <a href="http://dev.mysql.com/doc/refman/5.1/en/privileges-provided.html#priv_file"><code>FILE</code> privilege</a>. But <code>open_basedir</code> is definitely not safe. As seen in Debian&#8217;s php.ini default file: <em>This is considered a &#8220;broken&#8221; security measure. Applications relying on this feature will not recieve full support by the security team</em>&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2011/02/25/bypassing-phps-open_basedir-with-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Implementation of &#8220;tail -f&#8221; in PHP</title>
		<link>http://www.cloudconnected.fr/2011/01/29/implementation-of-tail-f-in-php/</link>
		<comments>http://www.cloudconnected.fr/2011/01/29/implementation-of-tail-f-in-php/#comments</comments>
		<pubDate>Sat, 29 Jan 2011 09:36:35 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=619</guid>
		<description><![CDATA[This is a small algorithm to implement a functionality similar to &#8220;tail -f&#8221; in PHP. The script is able watch a file in real time and do something everytime a new line is added (for example, a log file). It doesn&#8217;t implement the &#8220;tail&#8221; functionnality however (outputing only the end of the file) and instead [...]]]></description>
			<content:encoded><![CDATA[<p>This is a small algorithm to implement a functionality similar to &#8220;tail -f&#8221; in PHP. The script is able watch a file in real time and do something everytime a new line is added (for example, a log file). It doesn&#8217;t implement the &#8220;tail&#8221; functionnality however (outputing only the end of the file) and instead starts processing the file from the beginning.</p>
<pre>
$file = @ fopen($filename, 'r');
$pos = 0; 

while (true) {
	fseek($file, $pos);
	while ($line = fgets($file)) {
		// do something with $line
	}
	$pos = ftell($file);
	sleep(1);
}
fclose($file);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2011/01/29/implementation-of-tail-f-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Use destructor carefully</title>
		<link>http://www.cloudconnected.fr/2011/01/12/use-destructor-carefully/</link>
		<comments>http://www.cloudconnected.fr/2011/01/12/use-destructor-carefully/#comments</comments>
		<pubDate>Wed, 12 Jan 2011 10:45:24 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=611</guid>
		<description><![CDATA[A couple of days ago I lost half a day of work on a stupid bug in a PHP CLI script. Basically, the script wouldn&#8217;t die. Instead it would hang forever, eating all the CPU time. Even explicitly calling die() (or exit(), since they are synonyms) wouldn&#8217;t terminate it. Yep, that&#8217;s right, die() didn&#8217;t work! [...]]]></description>
			<content:encoded><![CDATA[<p>A couple of days ago I lost half a day of work on a stupid bug in a PHP CLI script. Basically, the script wouldn&#8217;t die. Instead it would hang forever, eating all the CPU time. Even explicitly calling <code>die()</code> (or <code>exit()</code>, since they are synonyms) wouldn&#8217;t terminate it. Yep, that&#8217;s right, <code>die()</code> didn&#8217;t work! Until then, I naively believed that <code>die()</code> was some failsafe language construct that would terminate the script not matter what. But as it turned out, it&#8217;s not the case&#8230;</p>
<p>According to PHP documentation: <q><em>The destructor will be called even if script execution is stopped using exit()</em></q>. So destructor&#8217;s code will be executed even after <code>die()</code> is called. What happens if a destructor&#8217;s code is faulty and gets stuck in an infinite loop ? The script will never end. In my story, I was using an third-party object-oriented library (for AMQP) that did some funky stuff in some objects destructor, and then waited forever for an event on the network&#8230; Needless to say, it&#8217;s a BAD idea to write that much application logic in a destructor.</p>
<p>So the moral of the story is: (1) <code>die()</code> can fail and (2) use destructor with caution, only write code that will NEVER fail and that is STRICLY necessary like closing connections, closing file handlers and such. Same goes for <a href="http://www.php.net/register_shutdown_function" rel="nofollow">shutdown functions</a> by the way.</p>
<p>A quick example for the sake of demonstration:</p>
<pre>
class Evil
{
	public function __destruct()
	{
		while (true);
	}
}

$foo = new Evil();
die();
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2011/01/12/use-destructor-carefully/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>One PHP error log per developer</title>
		<link>http://www.cloudconnected.fr/2010/09/16/one-php-error-log-per-developer/</link>
		<comments>http://www.cloudconnected.fr/2010/09/16/one-php-error-log-per-developer/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 15:50:09 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=608</guid>
		<description><![CDATA[At work we&#8217;re using a shared web development server, with a setup that allows every developer to have his own compartmentalized development space. It requires some tricky configurations, but in the end I find it way more efficient than the policy &#8220;every developer installs its own Apache/PHP/MySQL stack&#8221;. I already talked about the general ideas [...]]]></description>
			<content:encoded><![CDATA[<p>At work we&#8217;re using a shared web development server, with a setup that allows every developer to have his own compartmentalized development space. It requires some tricky configurations, but in the end I find it way more efficient than the policy &#8220;every developer installs its own Apache/PHP/MySQL stack&#8221;. I already talked about the general ideas <a href="/2008/07/08/developpement-web-collaboratif/">in a previous article </a> (in french) two years ago, and so far it works like a charm. Our setup it constantly improving, and today&#8217;s big question is: how to make so that every developer has it&#8217;s own PHP error log?</p>
<h3>Apache config</h3>
<p>First, a little insight on the Apache configuration. If we would be using one virtual host directive per developer, this wouldn&#8217;t be an issue at all. Just set up the proper <code>error_log</code> path (using Apache&#8217;s <code>php_value</code> directive like so: <code>php_value error_log /var/log/php/errors_username.log</code>) for every virtual host, reload Apache and you&#8217;re done.</p>
<p>Our Apache, however, is using <code>mod_vhost_alias</code>. Basically we have &#8220;one virtual host to rule them all&#8221;, because I&#8217;m lazy and I don&#8217;t want to write one virtual host for every new dev, or modify a dozens of virtual host directives everytime I change a setting.</p>
<p>So how does it works? The first part of the URL is the developer username, then the second part is the project&#8217;s folder name, then the remaining part is the dev server hostname. With the <code>VirtualDocumentRoot</code> directive, you use parts of the URL has variable for calculating the correct document root path. (Actually, our setup includes a third part which is the subfolder to separate the document root of a project from all the include files, but that&#8217;s not the point of this article). Example:</p>
<pre>
&lt;VirtualHost *:80&gt;
	ServerName dev.domain.com
	ServerAlias *.dev.domain.com

	UseCanonicalName off
	VirtualDocumentRoot /home/%1/web/%2

	&lt;Directory /home/*/web/*/*&gt;
		Allow from all
		AllowOverride all
	&lt;/Directory&gt;
&lt;/VirtualHost&gt;
</pre>
<p>The address <code>http://remi.awesome.dev.domain.com</code> will automagically has a document root located in: <code>/home/remi/web/awesome</code>. The benefit is obvious: simplicity and flexibilty. I love <a href="http://en.wikipedia.org/wiki/Convention_over_configuration">convention over configuration</a> design principle.</p>
<h3>PHP config</h3>
<p>Now the problem is that it&#8217;s impossible to tell PHP to logs the errors on a different file based on the first part of the URL directly into the Apache configuration, because the <code>%1</code>, <code>%2</code>,&#8230; variable are only valid inside the <code>VirtualDocumentRoot</code> directive.</p>
<p>So we need to do it inside PHP or inside an <code>htaccess</code> file on every folder. But doing it for every project is a pain, not to mention the conflicts to come in SVN because every developer will have his own path. That&#8217;s when PHP&#8217;s <code>auto_prepend_file</code> option come into play (and honestly, I would never have thought it&#8217;ll be useful one day).</p>
<p>I just create a file named <code>/etc/php5/prepend.php</code> with that one-liner:</p>
<pre>
&lt;?php ini_set('error_log',sprintf('/var/log/php/errors_%s.log',substr($_SERVER['SERVER_NAME'], 0, strpos($_SERVER['SERVER_NAME'],'.'))));
</pre>
<p>And add this settings to <code>php.ini</code>:</p>
<pre>
auto_prepend_file = /etc/php5/prepend.php
</pre>
<p>Note that the directory <code>/var/log/php</code> will not be created out of the blue, so you have to take care of it, and it must be writeable by <code>www-data</code>. Files will be named <code>errors_(first part of the url).log</code>, so for example <code>errors_remi.log</code> in the previous example. Users belongs to group <code>www-data</code>, so as long as the logs are readable by the group (what they are by default) they&#8217;ll be able to read their logs.</p>
<p>And now the cherry on top of the cake, the logrotate configuration to rotate logs weekly, and keep one month (4 files) of archive:</p>
<pre>
/var/log/php/errors_*.log {
        weekly
        missingok
        rotate 4
        compress
        delaycompress
        notifempty
        create 640 www-data www-data
}
</pre>
<p>Et voilà!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2010/09/16/one-php-error-log-per-developer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google Chart PHP Library 0.4</title>
		<link>http://www.cloudconnected.fr/2010/05/15/google-chart-php-library-0-4/</link>
		<comments>http://www.cloudconnected.fr/2010/05/15/google-chart-php-library-0-4/#comments</comments>
		<pubDate>Sat, 15 May 2010 08:20:37 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[chart]]></category>
		<category><![CDATA[googlechart]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=599</guid>
		<description><![CDATA[I released a new version of my PHP library for Google Chart API. Remember it&#8217;s still in heavy development (well, depending on my free time and my motivation, so &#8220;heavy&#8221; is relative), therefore don&#8217;t except anything bug-free or feature complete. Less rigid API Until this version, I was focusing on implementing strictly the Google Chart [...]]]></description>
			<content:encoded><![CDATA[<p>I released a new version of <a href="/2010/04/28/announcing-googlechart-php-library-0-3/">my PHP library for Google Chart API</a>. Remember it&#8217;s still in heavy development (well, depending on my free time and my motivation, so &#8220;heavy&#8221; is relative), therefore don&#8217;t except anything bug-free or feature complete.</p>
<h3>Less rigid API</h3>
<p>Until this version, I was focusing on implementing strictly the Google Chart API. However, I eventually realize that the API is sometimes too rigid. For example, say you want to hide an axis. You have to specify &#8220;<code>_</code>&#8221; (underscore) as the 5th value (<code>axis_or_tick</code>) in the <code>chxs</code> parameter (values are separated by a coma). Looks easy right? Except it is NOT ok to omit the first 4th values. So you have to specify the <code>label_color</code>, <code>font_size</code> and <code>alignment</code> before, in order to be able to hide your axis.</p>
<p>In version 0.3, you had to do exactly that, by using <code>setStyle</code> and specifying the 4th parameter (<code>$axis_or_tick</code>). If you wonder why it&#8217;s not the 5th, it&#8217;s because the &#8220;axis index&#8221; value is calculated on runtime. Fortunatly, you could pass <code>null</code> as the value for the parameter, and the library will replace them by the default value. Example:</p>
<pre>
$axis = new GoogleChartAxis('x');
$axis->setStyle(null, null, null, '_');
</pre>
<p>In version 0.4, the <code>setStyle</code> method as been removed and splitted into multiple methods <code>setLabelColor</code>, <code>setFontSize</code>, <code>setLabelAlignment</code>, <code>setDrawLine</code>, <code>setDrawTickMarks</code> and <code>setTickColor</code>. Now you don&#8217;t need to worry about how many parameter you have to set, just call the method you want and the library will take care of setting the appropriate intermediate values. Example:</p>
<pre>
$axis = new GoogleChartAxis('x');
$axis->setDrawLine(false)->setDrawTickMarks(false);
</pre>
<h3>More abstraction</h3>
<p>This version also comes up with a set of features to simplify chart creation. For example, one of my favorite is <code>setBorder</code> method for Shape Markers (<code>GoogleChartShapeMarker</code>).</p>
<p>To create a border in a shape with Google Chart API, you need to create another similar marker below the first one (think z-order), with a different color and a slightly bigger size. Well, this can be done exactly this way in version 0.3. However, starting version 0.4, the <code>setBorder</code> method does the job for you. Just specify a color and the size of the border, and it will create the second marker automatically. Not only this is more convenient and easy to write, but this is also faster and uses less memory.</p>
<h3>New features</h3>
<p>This version adds support for Dynamic Icon. Because icons can be either &#8220;freestanding&#8221; (used as a chart) or used as marker, I had to refactor the base class. Now the base class is <code>GoogleChartApi</code> which holds the logic to query the API. <code>GoogleChart</code> and <code>GoogleChartIcon</code> extends this class, so you can use a <code>GoogleChartIcon</code> exactly the same way as a chart.</p>
<p>Example:</p>
<pre>
require '../lib/icons/GoogleChartIconNote.php';

$chart = new GoogleChartIconNote('Hello world');
$chart->setTitle('Example');
$chart->setTextColor('D01F3C');

header('Content-Type: image/png');
echo $chart;
</pre>
<p>To use a icon as a marker, use the new <code>addDynamicMarker</code> method. Example:</p>
<pre>
require '../lib/GoogleChart.php';
require '../lib/icons/GoogleChartIconNote.php';

$values = array();
for ($i = 0; $i &lt;= 10; $i += 1) {
	$values[] = rand(20,80);
}

$chart = new GoogleChart('ls', 500, 200);
$data = new GoogleChartData($values);
$chart->addData($data);

$marker = new GoogleChartIconNote('Hello');
$marker->setData($data);
$chart->addDynamicMarker($marker);

header('Content-Type: image/png');
echo $chart;
</pre>
<p>For the moment, only &#8220;note&#8221; icon (aka <a href="http://code.google.com/intl/fr-FR/apis/chart/docs/gallery/dynamic_icons.html#fun_style_note" rel="nofollow">Fun style notes with text and optional title</a>) are supported, but I&#8217;m working on it.</p>
<h3>Hey, the project has a new home!</h3>
<p>Yes, the project is now hosted by Google Code. Because there is already a shitload of abandonned projects named using every possible combinations of &#8220;google&#8221; &#8220;chart&#8221; and &#8220;php&#8221;, I had to use the (rather long) name &#8220;googlechartphplib&#8221;. So new home is here:<br />
<a href="http://code.google.com/p/googlechartphplib">http://code.google.com/p/googlechartphplib</a>. You&#8217;ll find source code, issue tracker, documentation and the new SVN access there.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2010/05/15/google-chart-php-library-0-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Announcing GoogleChart PHP library 0.3</title>
		<link>http://www.cloudconnected.fr/2010/04/28/announcing-googlechart-php-library-0-3/</link>
		<comments>http://www.cloudconnected.fr/2010/04/28/announcing-googlechart-php-library-0-3/#comments</comments>
		<pubDate>Wed, 28 Apr 2010 15:02:39 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[googlechart]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.cloudconnected.fr/?p=586</guid>
		<description><![CDATA[I&#8217;ve been playing around a lot with Google Chart API lately, mainly for fun to draw some charts based on my Last.fm profile (Last.fm provides a very nice API). Google Chart API is very powerful, but quite harsh to work with, and unfortunately I didn&#8217;t find any good and easy-to-use PHP library for it. There [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been playing around a lot with <a href="http://code.google.com/apis/chart/image_charts.html">Google Chart API</a> lately, mainly for fun to draw some charts based on my Last.fm profile (Last.fm provides a <a href="http://www.last.fm/api">very nice API</a>). Google Chart API is very powerful, but quite harsh to work with, and unfortunately I didn&#8217;t find any good and easy-to-use PHP library for it. There are <a href="https://groups.google.com/group/google-chart-api/web/useful-links-to-api-libraries?_done=/group/google-chart-api&#038;pli=1">some</a> but most of them are either not maintened or not fully working. So I ended up writting my own library. A few weeks ago, I used it for a project at work, improved it a bit and it worked like a charm. Eventually I decided to release it open-source (MIT license). It&#8217;s provided &#8220;as is&#8221;, without warranty of any kind. I just hope that it might be useful to somebody else as well, who knows?</p>
<h3>Quick introduction</h3>
<p>The library&#8217;s goal is to provide an easy way to build requests to Google Chart API, and especially to ease the painfull strings concatenation with comas, pipes, colons, etc. If you&#8217;ve already tried Google Chart API, you know what I mean! :-) So I wrote a couple of class, that allows to quickly create a chart (<code>GoogleChart</code>), add data series (<code>GoogleChartData</code>), axes (<code>GoogleChartAxis</code>) and markers (<code>GoogleChartMarker</code>), and compute an URL (for GET requests) or an array of parameters (for POST requests). It can even fetch the image for you (via GET or POST) so that you can display it directly (or cache it, or do whatever you want with it).</p>
<p><span id="more-586"></span></p>
<p>I wanted to stick really closely to the actual Google Chart API. I hate having to fight with a library when I wanted to do some fancy stuffs with the API. So most of the time, the library will only provides an set of convenient setters stricly mapped on the API specification with almost no additional logic (there is also an universal setter if the setter you need is unimplemented yet. ;-) But YOU will have to check in the Google documentation for the list of supported parameters, or how to do some advanced stuff (compound charts, anyone?). If you give an unsupported parameter, you might get some unexepected results from Google Chart API. Only the cases that would makes the API send a error are checked.</p>
<p>In other words: this library is only an interface, the PHP logic is kept to the minimum. One exception here though: the library provide an autoscaling feature, because scaling manually your chart is a pain in the ass (this feature can be disabled if you want). Therefore if you&#8217;re not ready to read at least some of the Google Chart API documentation, this tool might not be for you.</p>
<h3>A quick example</h3>
<p>One quick example on how to create a line chart with 3 lines, 2 axis and some data markers.</p>
<pre>
require '../lib/GoogleChart.php';
require '../lib/markers/GoogleChartTextMarker.php';
require '../lib/markers/GoogleChartShapeMarker.php';

// create some random values
$values = array(
	array(),
	array(),
	array()
);
$n = 10;
for ($i = 0; $i &lt;= $n; $i += 1) {
	$v = rand($i , $i*10);
	$values[0][] = $v;
	$values[1][] = $v - $i;
	$values[2][] = rand(100 - ($i+10),100 - 10*$i);
}

// create the chart and define some options
$chart = new GoogleChart('lc', 400, 200);
$chart-&gt;setGridLines(10,10);
$chart-&gt;setLegendPosition('r');
$chart-&gt;setFill('ffffcc');
$chart-&gt;setGradientFill(45, array('cccccc', 'ffffff', 'cccccc'), GoogleChart::CHART_AREA);
$chart-&gt;setTitle('Us versus the others.');
$chart-&gt;setTitleStyle('999999', 20);

// create the first line
$line = new GoogleChartData($values[0]);
$line-&gt;setLegend('Us');
$chart-&gt;addData($line);

// add markers to this line
$marker = new GoogleChartShapeMarker(GoogleChartShapeMarker::X);
$marker-&gt;setData($line);
$marker-&gt;setColor('6699cc');
$chart-&gt;addMarker($marker);

$marker = new GoogleChartTextMarker(GoogleChartTextMarker::VALUE);
$marker-&gt;setData($line);
$chart-&gt;addMarker($marker);

// define a dotted line (not displayed in the legend)
$line = new GoogleChartData($values[1]);
$line-&gt;setStyle(2,2,2);
$line-&gt;setColor('6699cc');
$chart-&gt;addData($line);

// define a red line
$line = new GoogleChartData($values[2]);
$line-&gt;setLegend('The others');
$line-&gt;setColor('ff0000');
$chart-&gt;addData($line);

// add markers
$marker = new GoogleChartShapeMarker(GoogleChartShapeMarker::CIRCLE);
$marker-&gt;setData($line);
$marker-&gt;setColor('ff0000');
$chart-&gt;addMarker($marker);

// add axis
$y_axis = new GoogleChartAxis('y');
$chart-&gt;addAxis($y_axis);

$x_axis = new GoogleChartAxis('x');
$chart-&gt;addAxis($x_axis);

// debug the chart
if ( isset($_GET['debug']) ) {
	var_dump($chart->getQuery());
	echo $chart->validate();
	echo $chart->toHtml();
}
// display the chart
else{
	header('Content-Type: image/png');
	echo $chart;
}
</pre>
<p>Generated chart (I copy/pasted the generated URL, this is not the actual PHP code):</p>
<p><img src="http://chart.apis.google.com/chart?cht=lc&#038;chs=400x200&#038;chg=10,10&#038;chf=bg,s,ffffcc|c,lg,45,cccccc,0,ffffff,0.5,cccccc,1&#038;chtt=Us+versus+the+others.&#038;chts=999999,20&#038;chd=t:0,10,11,24,31,27,7,34,28,18,59|0,9,9,21,27,22,1,27,20,9,49|95,90,84,75,74,72,42,71,51,12,46&#038;chco=4D89F9,6699cc,ff0000&#038;chls=2|2,2,2|2&#038;chdl=Us||The+others&#038;chdlp=rs&#038;chm=x,6699cc,0,-1,10|N,336699,0,-1,10|o,ff0000,2,-1,10&#038;chxt=y,x" alt="Result chart" /></p>
<h3>Requirements and limitations</h3>
<p>GoogleChart PHP library works with PHP 5.2 (maybe works with other version as well, I don&#8217;t know), and doesn&#8217;t require any additional library. It&#8217;s released under the MIT license. Current version is 0.3, meaning that a lot of features are unimplemented yet (but otherwise it works fine, believe me ;-). Current major limitations includes :</p>
<ul>
<li>Only support <a href="http://code.google.com/apis/chart/docs/data_formats.html#text">Basic Text Format</a> and <a href="http://code.google.com/apis/chart/docs/data_formats.html#data_scaling">Text Format with Custom Scaling</a>.</li>
<li>Only tested with <a href="http://code.google.com/apis/chart/docs/gallery/line_charts.html">Line Charts</a>, <a href="http://code.google.com/apis/chart/docs/gallery/map_charts.html">Map Charts</a> and <a href="http://code.google.com/apis/chart/docs/gallery/bar_charts.html">Bar Charts</a>. It&#8217;s designed to handle any type though, so with a little bit of luck it might works for other charts as well.</li>
</ul>
<p>And also, because it&#8217;s a side project I do during my spare time, I only implement the features that I need for my other projets, and only when I need them. So if have different needs and you&#8217;re willing to help, you&#8217;re welcome!</p>
<h3>Download and install</h3>
<p>GoogleChart PHP library is available for download here: <del>http://redmine.kuerti.net/projects/googlechart/files</del> <a href="http://code.google.com/p/googlechartphplib">http://code.google.com/p/googlechartphplib</a>. The archive comes with plenty examples and Doxygen documentation. Just copy the content of the <code>lib</code> folder inside your project.</p>
<p>You can also checkout the latest SVN version here: <del><code>http://svn.kuerti.net/googlechart/trunk</code></del> <code>http://googlechartphplib.googlecode.com/svn/trunk/</code>.</p>
<p>Doxygen-generated <a href="http://googlechartphplib.cloudconnected.fr/doc/">documentation is available here</a>. This documentation is not always up-to-date nor complete, but I&#8217;m working on it, I promise.</p>
<h3>Send me feebacks, I love feedbacks</h3>
<p>Feedbacks are more than welcome! Feel to send feature requests, bug reports and more here as a comment, or (better) on <del>the project&#8217;s Redmine: http://redmine.kuerti.net/projects/googlechart</del> the project homepage in Google Code: <a href="http://code.google.com/p/googlechartphplib/">http://code.google.com/p/googlechartphplib/</a>. And if you&#8217;re a developer and interested on improving it, feel free, it&#8217;s open source! Mails are also always welcome at <code>remi at cloudconnected dot fr</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2010/04/28/announcing-googlechart-php-library-0-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Indentation avec des espaces pour les fichiers YAML avec SciTE</title>
		<link>http://www.cloudconnected.fr/2009/07/22/indentation-avec-des-espaces-pour-les-fichiers-yaml-avec-scite/</link>
		<comments>http://www.cloudconnected.fr/2009/07/22/indentation-avec-des-espaces-pour-les-fichiers-yaml-avec-scite/#comments</comments>
		<pubDate>Wed, 22 Jul 2009 08:06:45 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[indentation]]></category>
		<category><![CDATA[scite]]></category>
		<category><![CDATA[yaml]]></category>

		<guid isPermaLink="false">http://www.the-asw.com/?p=547</guid>
		<description><![CDATA[Vous utilisez SciTE comme éditeur de code, et vous l&#8217;avez configuré pour utiliser des tabulations pour l&#8217;indentation dans votre coding style ? C&#8217;est parfait, vous êtes sur la voix de la sagesse. Mais voilà qu&#8217;un beau jour débarquent dans votre petite vie tranquille de développeur les fichiers au format YAML (Yet Another Markup Language), format [...]]]></description>
			<content:encoded><![CDATA[<p>Vous utilisez <a href="/2005/11/11/scite-l-editeur-indispensable/">SciTE</a> comme éditeur de code, et vous l&#8217;avez configuré pour <a href="/2005/10/14/vous-avez-dit-«-coding-style-»/">utiliser des tabulations pour l&#8217;indentation dans votre coding style</a> ? C&#8217;est parfait, vous êtes sur la voix de la sagesse. Mais voilà qu&#8217;un beau jour débarquent dans votre petite vie tranquille de développeur les fichiers au format YAML (Yet Another Markup Language), format qui impose l&#8217;utilisation d&#8217;espace pour l&#8217;indentation, sous peine de rendre le fichier invalide. Saloperie. Heureusement, il suffit de quelques lignes de configuration pour faire en sorte que SciTE se comporte correctement avec les fichiers YAML :</p>
<pre>
use.tabs.*.yml=0
indent.size.*.yml=2
tab.size.*.yml=2
</pre>
<p>Ouf !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2009/07/22/indentation-avec-des-espaces-pour-les-fichiers-yaml-avec-scite/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Trees in SQL : an approach based on materialized paths and normalization for MySQL</title>
		<link>http://www.cloudconnected.fr/2009/05/26/trees-in-sql-an-approach-based-on-materialized-paths-and-normalization-for-mysql/</link>
		<comments>http://www.cloudconnected.fr/2009/05/26/trees-in-sql-an-approach-based-on-materialized-paths-and-normalization-for-mysql/#comments</comments>
		<pubDate>Tue, 26 May 2009 16:13:11 +0000</pubDate>
		<dc:creator>Rémi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://www.the-asw.com/?p=534</guid>
		<description><![CDATA[I&#8217;m working since a few months on how storing trees in MySQL. It started like &#8220;hey, let&#8217;s make a database of every genre of heavy metal music!&#8221;. So I began with the easy way : a tree structure in a table genre with id and parent_id columns. And then I came to the point where [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working since a few months on how storing trees in MySQL. It started like &#8220;hey, let&#8217;s make a database of every genre of heavy metal music!&#8221;. So I began with the easy way : a tree structure in a table <code>genre</code> with <code>id</code> and <code>parent_id</code> columns. And then I came to the point where I wanted to display the entire tree. This is just impossible with a reasonable number of queries, as there is no recursive syntax in standard SQL nor in MySQL (Oracle has the <code>CONNECT BY</code> extension). So I started researching the web.</p>
<p>There are basically three ways to store a tree in a relationnal databases : <strong>adjacency list</strong>,  <strong>nested set</strong> and <strong>materialized path</strong>, plus a few more like <strong>nested intervals</strong>. Let&#8217;s put it clear : they all suck.</p>
<ul>
<li><strong>Adjacency list</strong> model is the one I used the first time (with <code>parent_id</code> column). It&#8217;s easy to create but impossible to query deeply without recursivity.</li>
<li><strong>Nested set</strong> model is quite easy to query, but is indeed pretty fucked up (tree is limited in size, very hard to read without maths, almost all the lines of the table need to be updated each time a node is added, moved or deleted!). <strong>Nested intervals</strong> model tries to remove the size limitation of <strong>nested sets</strong> model, but involves way to much maths.</li>
<li><strong>Materialized path</strong> is great and easy to understand, but very slow because it involves heavy use of the &#8220;LIKE&#8221; operator.</ul>
<p>More on that <a href="http://dev.mysql.com/tech-resources/articles/hierarchical-data.html">here</a>, <a href="http://www.dbazine.com/oracle/or-articles/tropashko4">here</a> and <a href="http://www.ibstaff.net/fmartinez/?p=18">here</a>.</p>
<p>Then I found two very interesting articles. One is <a href="http://forums.mysql.com/read.php?125,101885,101954#msg-101954">a reply on MySQL Forums about materialized path performance problem</a>, recommending an approach based on normalized schema to store paths. The other is an <a href="http://www.depesz.com/index.php/2008/04/11/my-take-on-trees-in-sql/">implementation using PostgreSQL by depesz</a> that also use a normalized schema. The point is to create a table dedicated to store all the paths from every nodes to every nodes. This looks to me like a very good approach, as it&#8217;s easy to understand, easy to maintain, easy to query and performance-ok (with appropriate indexes). Unfortunalty, the implementation I found relies to much on PostgreSQL and doesn&#8217;t work out-of-the-box with MySQL (because of the use of triggers and of some queries that needs to be rewriten). So I reworked it to work with MySQL 5, and changed a few things. I strongly recommend that you read <a href="http://www.depesz.com/index.php/2008/04/11/my-take-on-trees-in-sql/">depesz&#8217;s post</a> for a complete understanding of the approach before you continue.</p>
<p><span id="more-534"></span></p>
<h2>Data Model</h2>
<p>First, we create a table containing the data, let&#8217;s name it <code>data</code>. The <code>parent_id</code> column stores the parent&#8217;s ID, like in the traditionnal adjacency list approach, but we&#8217;re not going to use it for querying the tree. The <code>value</code> column is whatever you need to store as value for the node, and we&#8217;re going to use it for ordering. We use InnoDB as storage engine.</p>
<pre>
CREATE TABLE data (
	id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
	parent_id INTEGER UNSIGNED null,
	value VARCHAR(255) NOT NULL,

	PRIMARY KEY (id),

	INDEX data_data_fk (parent_id),
	FOREIGN KEY data_data_fk (parent_id) REFERENCES data (id) ON DELETE CASCADE
) ENGINE = InnoDB;
</pre>
<p>Then we create the table that will store every paths. A path contains a parent node, a child node, and the number of hops between the two nodes (depth). Each node has at least one entry on that table: the path to itself with a depth egal 0. The <code>path</code> column will hold the materialized path, that will be used for ordering. If you&#8217;re absolutely sure that you will never ever need to display your tree in alphabetical order, then you can safely remove this column and remove related queries from triggers.</p>
<pre>
CREATE TABLE data_tree (
	parent_id INTEGER UNSIGNED NOT NULL,
	child_id INTEGER UNSIGNED NOT NULL,
	depth INTEGER UNSIGNED NOT NULL default 0,
	path TEXT NOT NULL,

	PRIMARY KEY (parent_id, child_id),
	INDEX data_tree_child_depth_idx (child_id, depth),

	INDEX data_tree_parent_data_fk (parent_id),
	FOREIGN KEY data_tree_parent_data_fk (parent_id) REFERENCES data (id) ON DELETE CASCADE,
	INDEX data_tree_child_data_fk (child_id),
	FOREIGN KEY data_tree_child_data_fk (child_id) REFERENCES data (id) ON DELETE CASCADE,
);
</pre>
<h2>Creating a new node</h2>
<p>When a node is inserted in the table <code>data</code>, then every path leading to this node has to be inserted in the table <code>data_tree</code>. To achieve this, I use a trigger (MySQL 5 or higher) because it&#8217;s safer for the DB&#8217;s integrity, but a client side implementation (in PHP or any other langage) will do, as long as you use a transaction.</p>
<pre>
DELIMITER |
CREATE TRIGGER insert_data
AFTER INSERT ON data
FOR EACH ROW BEGIN
	INSERT INTO data_tree (parent_id, child_id, depth, path)
	VALUES (NEW.id, NEW.id, 0, CONCAT(NEW.value,"/"));

	INSERT INTO data_tree (parent_id, child_id, depth, path)
		SELECT parent_id, NEW.id, depth + 1, CONCAT(path, NEW.value, "/")
		FROM data_tree
		WHERE child_id = NEW.parent_id;
END; |
DELIMITER ;
</pre>
<h2>Updating the tree</h2>
<p>First possible update : moving a node. To move a node, we basically have to delete every path between the node&#8217;s ancestors (if any) to the node&#8217;s childs (if any), and then insert all the new paths. This can be done easily with two queries in a trigger, or again, if you prefer, client side. No big changes from <a href="http://www.depesz.com/index.php/2008/04/11/my-take-on-trees-in-sql/">depesz&#8217;s post</a>, I only had to rewrite queries for MySQL.</p>
<p>Second possible update : changing a node&#8217;s value. This step is optionnal if you decided to drop the <code>path</code> column. When a node&#8217;s value is updated, for exemple from &#8220;Apinat&#8221; to &#8220;Banaanit&#8221;, then every path including that node value has to be updated as well, otherwise the tree won&#8217;t be correctly ordered. This step has to be completly rewriten for MySQL, has the original <a href="http://www.depesz.com/index.php/2008/04/11/my-take-on-trees-in-sql/">depesz&#8217;s implementation for PostgreSQL</a> uses regexp replacements, which are not supported. Hence I came with a workaround, using only pure string functions to make the replacement.</p>
<pre>
DELIMITER |
CREATE TRIGGER update_data
AFTER UPDATE ON data
FOR EACH ROW BEGIN
	DECLARE old_path_len INT;
	IF NEW.parent_id != OLD.parent_id OR NEW.parent_id IS NULL OR OLD.parent_id IS NULL THEN
		if OLD.parent_id IS NOT NULL THEN
			DELETE t2
			FROM data_tree t1
			JOIN data_tree t2 ON t1.child_id = t2.child_id
			WHERE t1.parent_id = OLD.id
			AND t2.depth > t1.depth;
		END IF;

		IF NEW.parent_id IS NOT NULL THEN
			INSERT INTO data_tree (parent_id, child_id, depth, path)
				SELECT t1.parent_id, t2.child_id, t1.depth + t2.depth + 1, CONCAT(t1.path, t2.path)
				FROM data_tree t1, data_tree t2
				WHERE t1.child_id = NEW.parent_id
				AND t2.parent_id = OLD.id;
		END IF;
	END IF;

	IF NEW.value != OLD.value THEN
		SELECT CHAR_LENGTH(path) INTO old_path_len
		FROM data_tree
		WHERE child_id = OLD.id
		AND DEPTH = 0;

		IF old_path_len > 0 THEN
			UPDATE data_tree t1
			JOIN data_tree t2 ON t1.child_id = t2.child_id
			SET t2.path = CONCAT(
				SUBSTRING(t2.path, 1, CHAR_LENGTH(t2.path) - CHAR_LENGTH(t1.path)),
				CONCAT(NEW.value, SUBSTRING(t1.path, old_path_len))
			)
			WHERE t1.parent_id = OLD.id
			AND t2.depth >= t1.depth;
		END IF;
	END IF;
END; |
DELIMITER ;
</pre>
<h2>Deleting a node</h2>
<p>Thanks to the <code>ON DELETE CASCADE</code> clause, we don&#8217;t need to do anything when deleting a node. Beware that every childs will also be deleted from the tree <strong>and</strong> from the <code>data</code> table.</p>
<h2>Examples</h2>
<p>Let&#8217;s play a little with this implementation.</p>
<h3>Populating the tree</h3>
<pre>
mysql> INSERT INTO data (parent_id, value) VALUES (null, 'Heavy metal');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM data;
+----+-----------+-------------+
| id | parent_id | value       |
+----+-----------+-------------+
|  1 |      NULL | Heavy metal |
+----+-----------+-------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM data_tree;
+-----------+----------+-------+--------------+
| parent_id | child_id | depth | path         |
+-----------+----------+-------+--------------+
|         1 |        1 |     0 | Heavy metal/ |
+-----------+----------+-------+--------------+
1 row in set (0.00 sec)

mysql> INSERT INTO data (parent_id, value) VALUES (1, 'Power metal'), (1, 'Trash metal'), (1, 'Death metal');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM data;
+----+-----------+-------------+
| id | parent_id | value       |
+----+-----------+-------------+
|  1 |      NULL | Heavy metal |
|  2 |         1 | Power metal |
|  3 |         1 | Trash metal |
|  4 |         1 | Death metal |
+----+-----------+-------------+
4 rows in set (0.00 sec)

mysql> SELECT * FROM data_tree;
+-----------+----------+-------+--------------------------+
| parent_id | child_id | depth | path                     |
+-----------+----------+-------+--------------------------+
|         1 |        1 |     0 | Heavy metal/             |
|         1 |        2 |     1 | Heavy metal/Power metal/ |
|         1 |        3 |     1 | Heavy metal/Trash metal/ |
|         1 |        4 |     1 | Heavy metal/Death metal/ |
|         2 |        2 |     0 | Power metal/             |
|         3 |        3 |     0 | Trash metal/             |
|         4 |        4 |     0 | Death metal/             |
+-----------+----------+-------+--------------------------+
7 rows in set (0.00 sec)

mysql> INSERT INTO data (parent_id, value) VALUES (2, 'Symphonic power metal'), (2, 'Extreme power metal'), (4, 'Brutal death metal');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM data_tree;
+-----------+----------+-------+------------------------------------------------+
| parent_id | child_id | depth | path                                           |
+-----------+----------+-------+------------------------------------------------+
|         1 |        1 |     0 | Heavy metal/                                   |
|         1 |        2 |     1 | Heavy metal/Power metal/                       |
|         1 |        3 |     1 | Heavy metal/Trash metal/                       |
|         1 |        4 |     1 | Heavy metal/Death metal/                       |
|         1 |        5 |     2 | Heavy metal/Power metal/Symphonic power metal/ |
|         1 |        6 |     2 | Heavy metal/Power metal/Extreme power metal/   |
|         1 |        7 |     2 | Heavy metal/Death metal/Brutal death metal/    |
|         2 |        2 |     0 | Power metal/                                   |
|         2 |        5 |     1 | Power metal/Symphonic power metal/             |
|         2 |        6 |     1 | Power metal/Extreme power metal/               |
|         3 |        3 |     0 | Trash metal/                                   |
|         4 |        4 |     0 | Death metal/                                   |
|         4 |        7 |     1 | Death metal/Brutal death metal/                |
|         5 |        5 |     0 | Symphonic power metal/                         |
|         6 |        6 |     0 | Extreme power metal/                           |
|         7 |        7 |     0 | Brutal death metal/                            |
+-----------+----------+-------+------------------------------------------------+
16 rows in set (0.00 sec)

mysql> SELECT * FROM data;
+----+-----------+-----------------------+
| id | parent_id | value                 |
+----+-----------+-----------------------+
|  1 |      NULL | Heavy metal           |
|  2 |         1 | Power metal           |
|  3 |         1 | Trash metal           |
|  4 |         1 | Death metal           |
|  5 |         2 | Symphonic power metal |
|  6 |         2 | Extreme power metal   |
|  7 |         4 | Brutal death metal    |
+----+-----------+-----------------------+
7 rows in set (0.00 sec)
</pre>
<h3>Requesting the tree</h3>
<h4>First generation of childs FROM &#8220;Heavy metal&#8221;</h4>
<pre>
mysql> SELECT d.id, d.value
       FROM data_tree t
       JOIN data d ON d.id = t.child_id
       WHERE t.parent_id = 1
       AND depth = 1;
+----+-------------+
| id | value       |
+----+-------------+
|  2 | Power metal |
|  3 | Trash metal |
|  4 | Death metal |
+----+-------------+
3 rows in set (0.00 sec)
</pre>
<h4>Printing the whole tree, ordered</h4>
<pre>
mysql> SELECT CONCAT(REPEAT('-',t.depth), d.value) AS value
       FROM data_tree t
       JOIN data d ON d.id = t.child_id
       WHERE t.parent_id = 1
       ORDER by t.path;
+-------------------------+
| value                   |
+-------------------------+
| Heavy metal             |
| -Death metal            |
| --Brutal death metal    |
| -Power metal            |
| --Extreme power metal   |
| --Symphonic power metal |
| -Trash metal            |
+-------------------------+
7 rows in set (0.00 sec)
</pre>
<h4>Ancestors of &#8220;Symphonic power metal&#8221;, starting from the root</h4>
<pre>
mysql> SELECT d.id, d.value
       FROM data_tree t
       JOIN data d ON d.id = t.parent_id
       WHERE t.child_id = 5
       ORDER BY depth DESC;
+----+-----------------------+
| id | value                 |
+----+-----------------------+
|  1 | Heavy metal           |
|  2 | Power metal           |
|  5 | Symphonic power metal |
+----+-----------------------+
3 rows in set (0.00 sec)
</pre>
<h3>Updating the tree</h3>
<h4>Moving a node to another place</h4>
<p>Let&#8217;s add a Hard rock in our tree, and move Heavy metal as a child of Hard rock.</p>
<pre>
mysql> INSERT INTO data (parent_id, value) VALUES (null, 'Hard rock');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM data;
+----+-----------+-----------------------+
| id | parent_id | value                 |
+----+-----------+-----------------------+
|  1 |      NULL | Heavy metal           |
|  2 |         1 | Power metal           |
|  3 |         1 | Trash metal           |
|  4 |         1 | Death metal           |
|  5 |         2 | Symphonic power metal |
|  6 |         2 | Extreme power metal   |
|  7 |         4 | Brutal death metal    |
|  8 |      NULL | Hard rock             |
+----+-----------+-----------------------+
8 rows in set (0.00 sec)

mysql> UPDATE data SET parent_id = 8 WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM data;
+----+-----------+-----------------------+
| id | parent_id | value                 |
+----+-----------+-----------------------+
|  1 |         8 | Heavy metal           |
|  2 |         1 | Power metal           |
|  3 |         1 | Trash metal           |
|  4 |         1 | Death metal           |
|  5 |         2 | Symphonic power metal |
|  6 |         2 | Extreme power metal   |
|  7 |         4 | Brutal death metal    |
|  8 |      NULL | Hard rock             |
+----+-----------+-----------------------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM data_tree;
+-----------+----------+-------+----------------------------------------------------------+
| parent_id | child_id | depth | path                                                     |
+-----------+----------+-------+----------------------------------------------------------+
|         1 |        1 |     0 | Heavy metal/                                             |
|         1 |        2 |     1 | Heavy metal/Power metal/                                 |
|         1 |        3 |     1 | Heavy metal/Trash metal/                                 |
|         1 |        4 |     1 | Heavy metal/Death metal/                                 |
|         1 |        5 |     2 | Heavy metal/Power metal/Symphonic power metal/           |
|         1 |        6 |     2 | Heavy metal/Power metal/Extreme power metal/             |
|         1 |        7 |     2 | Heavy metal/Death metal/Brutal death metal/              |
|         2 |        2 |     0 | Power metal/                                             |
|         2 |        5 |     1 | Power metal/Symphonic power metal/                       |
|         2 |        6 |     1 | Power metal/Extreme power metal/                         |
|         3 |        3 |     0 | Trash metal/                                             |
|         4 |        4 |     0 | Death metal/                                             |
|         4 |        7 |     1 | Death metal/Brutal death metal/                          |
|         5 |        5 |     0 | Symphonic power metal/                                   |
|         6 |        6 |     0 | Extreme power metal/                                     |
|         7 |        7 |     0 | Brutal death metal/                                      |
|         8 |        1 |     1 | Hard rock/Heavy metal/                                   |
|         8 |        2 |     2 | Hard rock/Heavy metal/Power metal/                       |
|         8 |        3 |     2 | Hard rock/Heavy metal/Trash metal/                       |
|         8 |        4 |     2 | Hard rock/Heavy metal/Death metal/                       |
|         8 |        5 |     3 | Hard rock/Heavy metal/Power metal/Symphonic power metal/ |
|         8 |        6 |     3 | Hard rock/Heavy metal/Power metal/Extreme power metal/   |
|         8 |        7 |     3 | Hard rock/Heavy metal/Death metal/Brutal death metal/    |
|         8 |        8 |     0 | Hard rock/                                               |
+-----------+----------+-------+----------------------------------------------------------+
24 rows in set (0.00 sec)

mysql> SELECT CONCAT(REPEAT('-',t.depth), d.value) AS value
       FROM data_tree t
       JOIN data d ON d.id = t.child_id
       WHERE t.parent_id = 8
       ORDER BY t.path;
+--------------------------+
| value                    |
+--------------------------+
| Hard rock                |
| -Heavy metal             |
| --Death metal            |
| ---Brutal death metal    |
| --Power metal            |
| ---Extreme power metal   |
| ---Symphonic power metal |
| --Trash metal            |
+--------------------------+
8 rows in set (0.00 sec)
</pre>
<h4>Changing a node&#8217;s value</h4>
<p>Ok, I don&#8217;t know why we would do that, but let&#8217;s just rename Death metal to ZDeath metal and see what happens.</p>
<pre>
mysql> UPDATE data SET value = 'ZDeath metal' WHERE id = 4;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM data_tree;
+-----------+----------+-------+----------------------------------------------------------+
| parent_id | child_id | depth | path                                                     |
+-----------+----------+-------+----------------------------------------------------------+
|         1 |        1 |     0 | Heavy metal/                                             |
|         1 |        2 |     1 | Heavy metal/Power metal/                                 |
|         1 |        3 |     1 | Heavy metal/Trash metal/                                 |
|         1 |        4 |     1 | Heavy metal/ZDeath metal/                                |
|         1 |        5 |     2 | Heavy metal/Power metal/Symphonic power metal/           |
|         1 |        6 |     2 | Heavy metal/Power metal/Extreme power metal/             |
|         1 |        7 |     2 | Heavy metal/ZDeath metal/Brutal death metal/             |
|         2 |        2 |     0 | Power metal/                                             |
|         2 |        5 |     1 | Power metal/Symphonic power metal/                       |
|         2 |        6 |     1 | Power metal/Extreme power metal/                         |
|         3 |        3 |     0 | Trash metal/                                             |
|         4 |        4 |     0 | ZDeath metal/                                            |
|         4 |        7 |     1 | ZDeath metal/Brutal death metal/                         |
|         5 |        5 |     0 | Symphonic power metal/                                   |
|         6 |        6 |     0 | Extreme power metal/                                     |
|         7 |        7 |     0 | Brutal death metal/                                      |
|         8 |        1 |     1 | Hard rock/Heavy metal/                                   |
|         8 |        2 |     2 | Hard rock/Heavy metal/Power metal/                       |
|         8 |        3 |     2 | Hard rock/Heavy metal/Trash metal/                       |
|         8 |        4 |     2 | Hard rock/Heavy metal/ZDeath metal/                      |
|         8 |        5 |     3 | Hard rock/Heavy metal/Power metal/Symphonic power metal/ |
|         8 |        6 |     3 | Hard rock/Heavy metal/Power metal/Extreme power metal/   |
|         8 |        7 |     3 | Hard rock/Heavy metal/ZDeath metal/Brutal death metal/   |
|         8 |        8 |     0 | Hard rock/                                               |
+-----------+----------+-------+----------------------------------------------------------+
24 rows in set (0.00 sec)

mysql> SELECT CONCAT(REPEAT('-',t.depth), d.value) AS value
       FROM data_tree t
       JOIN data d ON d.id = t.child_id
       WHERE t.parent_id = 8
       ORDER by t.path;
+--------------------------+
| value                    |
+--------------------------+
| Hard rock                |
| -Heavy metal             |
| --Power metal            |
| ---Extreme power metal   |
| ---Symphonic power metal |
| --Trash metal            |
| --ZDeath metal           |
| ---Brutal death metal    |
+--------------------------+
8 rows in set (0.00 sec)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cloudconnected.fr/2009/05/26/trees-in-sql-an-approach-based-on-materialized-paths-and-normalization-for-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

