<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

	<title>jdbartlett | mediumtext</title>
	<link href="http://jdbartlett.github.com/atom.xml" rel="self" />
	<link href="http://jdbartlett.github.com/" />
	<id>http://jdbartlett.github.com/</id>
	<updated>2010-06-13T12:51:47-07:00</updated>
	<author>
		<name>joe bartlett</name>
		<email>blog@jdbartlett.com</email>
	</author>
	
	
		<entry>
			<title>TXJS Ear-Prickings & Burnings</title>
			<link href="http://jdbartlett.github.com/2010/06/10/txjs-good/" />
			<id>http://jdbartlett.github.com/2010/06/10/txjs-good/</id>
			<updated>2010-06-10T00:00:00-07:00</updated>
			<content type="html">&lt;p&gt;This post&amp;#8217;s a slapdash review of the &lt;a href=&quot;http://texasjavascript.com&quot;&gt;&lt;span class=&quot;caps&quot;&gt;TXJS&lt;/span&gt;&lt;/a&gt; sessions I attended, adapted from my Moleskinings &amp;#8212; things that pricked my ears and may burn the speakers&amp;#8217;. If you weren&amp;#8217;t able to attend, you may find the conference&amp;#8217;s website more useful, where slideshows and videos are being accumulated as they&amp;#8217;re uploaded.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://twitter.com/tomocchino&quot;&gt;@tomocchino:&lt;/a&gt; Front End Abstractions at Facebook&lt;/h3&gt;
&lt;p&gt;&amp;#8220;Early flushing&amp;#8221; isn&amp;#8217;t a phrase I&amp;#8217;d have elected to hear first thing after Friday night&amp;#8217;s party ;) but Facebook&amp;#8217;s doing some awesome stuff and I&amp;#8217;m embarrassed I hadn&amp;#8217;t properly looked into flushing techniques before &amp;#8212; it&amp;#8217;s even on Yahoo&amp;#8217;s best practices list.&lt;/p&gt;
&lt;p&gt;Facebook actually sends a skeleton of the page initially, and fills in the blanks progressively, using JavaScript to populate the skeleton as the server completes its tasks. The overall effect is a much faster experience for end-users, who can still view parts of the page even while it&amp;#8217;s loading. &lt;a href=&quot;http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919&quot;&gt;Another Facebook Engineer very recently posted an in-depth article on this,&lt;/a&gt; and &lt;a href=&quot;http://www.phpied.com/progressive-rendering-via-multiple-flushes/&quot;&gt;Stoyan has a good post on flush() itself.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I also liked Facebook&amp;#8217;s live()like method of looking for mousedowns, checking to see if the event applied to an anchor (and also for some other conditions), and then attaching the click event handler if true.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://twitter.com/jeresig&quot;&gt;@jeresig:&lt;/a&gt; JavaScript on the Mobile Web&lt;/h3&gt;
&lt;p&gt;&amp;#8220;We&amp;#8217;re on the cusp of the Golden Age of mobile web development.&amp;#8221;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;d forgotten all about playing with &lt;span class=&quot;caps&quot;&gt;WML&lt;/span&gt; as a kid in 1999, but it came flooding back to me in a flash when John said that. Little over a decade later: Mobile Safari &amp;amp; Android. &lt;a href=&quot;http://www.youtube.com/watch?v=8r1CZTLk-Gk&quot;&gt;Pretty amazing, really.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But for JavaScript, the mobile web&amp;#8217;s a rough place. John knows. But this session wasn&amp;#8217;t just his war stories from working on jQuery Mobile, it was useful, relevant information for anyone who wants to understand how to target websites for mobile devices. He&amp;#8217;s doing an incredible amount of work providing us, not just with a library that works on major supportable browsers, but with the tools to understand precisely what works, precisely what devices we need, and even grading devices and browsers so we can perform triage according to our needs and budgets.&lt;/p&gt;
&lt;p&gt;A few things that stood out to me:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;StatCounter and Gartner are the better sources for mobile browser stats.&lt;/li&gt;
	&lt;li&gt;But stats don&amp;#8217;t differentiate between browser versions, or even between Opera Mini &amp;amp; Opera Mobile, which are completely different browsers.&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://testswarm.com/&quot;&gt;Test Swarm&lt;/a&gt; is very very useful for automatic UI testing.&lt;/li&gt;
	&lt;li&gt;&amp;#8230;but interaction testing &lt;em&gt;must&lt;/em&gt; be done on a physical device. Even emulators are no substitute.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;John&amp;#8217;s releasing more information on &lt;a href=&quot;http://jquerymobile.com&quot;&gt;jQuery Mobile&lt;/a&gt; very soon, including (I believe) his list of 13 devices (with a cost breakdown), and details on browser support and browser popularity.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://twitter.com/joemccann&quot;&gt;@joemccann:&lt;/a&gt; Rapid Prototyping with JavaScript&lt;/h3&gt;
&lt;p&gt;Joe made a good case for JavaScript as a tool for prototyping apps. While people are using PhoneGap and Titanium to develop production applications, they and other JS tools are also useful for playing with desktop and mobile interfaces; for prototypes.&lt;/p&gt;
&lt;p&gt;But Joe really caught my attention when he started talking about &lt;span class=&quot;caps&quot;&gt;YQL&lt;/span&gt;, something that&amp;#8217;s been on my &amp;#8220;to investigate&amp;#8221; list for too long. He demonstrated YQL&amp;#8217;s ability to &amp;#8220;query&amp;#8221; a specific &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; (and other data sources) in a &lt;span class=&quot;caps&quot;&gt;SQL&lt;/span&gt;-like syntax and get back a &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; representation of your results; a sort of screen-scraper for the internet. Its application for prototyping is the rapid accumulation of oodles of real-life test data. Joe&amp;#8217;s example was a mobile web app he built in 10 minutes that scraped &lt;a href=&quot;http://crockfordfacts.com&quot;&gt;crockfordfacts.com&lt;/a&gt; and worked on Android and iPhone.&lt;/p&gt;
&lt;p&gt;Earlier this year, &lt;a href=&quot;http://twitter.com/codepo8&quot;&gt;@codepo8&lt;/a&gt; caught my eye with a post about &lt;a href=&quot;http://www.wait-till-i.com/2010/02/18/analysing-the-history-of-winter-olympics-medals-with-yql/&quot;&gt;querying the Guardian Data Blog using &lt;span class=&quot;caps&quot;&gt;YQL&lt;/span&gt;,&lt;/a&gt; and the amount of data and possibilities that blog presents, with YQL&amp;#8217;s help, are phenomenal.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://twitter.com/paulirish&quot;&gt;@paulirish:&lt;/a&gt; 10 Things I Learned from the jQuery Source&lt;/h3&gt;
&lt;p&gt;Paul&amp;#8217;s and Peter&amp;#8217;s talks were both great for the many intermediate-but-intimidated JS users out there. Most of us started using libraries (Prototype, jQuery, Dojo, etc.) before we properly understood how they&amp;#8217;re structured, or even how they work. Very often, we want to know precise information about something that jQuery does &amp;#8212; information that&amp;#8217;s only available in the source. Even if we don&amp;#8217;t &lt;em&gt;think&lt;/em&gt; we want to know, possibilities open up by simply reading the source (ah, jQuery devotionals) and learning its secrets.&lt;/p&gt;
&lt;p&gt;I could write several blog posts trying to summarize the session, but the main message was that jQuery&amp;#8217;s unminified source isn&amp;#8217;t inscrutable. This talk&amp;#8217;s a great leg-up for the many, many jQuery users who want to scrute it.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://twitter.com/stubbornella&quot;&gt;@stubbornella:&lt;/a&gt; The Top 5 Mistakes of Massive &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;Something Nicole really stressed: &amp;quot;If there were a &amp;#8220;&lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt;: The Good Parts&amp;#8221;, specificity would be an Awful Part.&amp;quot;&lt;/p&gt;
&lt;p&gt;I only recently started using YSlow, and the first thing I hit upon was overqualified selectors, specifically &amp;#8220;tagname.classname&amp;#8221;, a habit I picked up from tweaking &lt;a href=&quot;http://www.slideshare.net/paul.irish/perfcompression/29&quot;&gt;jQuery Sizzle selectors for performance.&lt;/a&gt; But Nicole made the case that &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; should describe visual patterns, and not just skins dependent on &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; for context.&lt;/p&gt;
&lt;p&gt;Nicole talked about how &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; becomes bloated and unmanageable, usually by thinking in terms of stylesheets that reflect semantic markup instead of visual patterns. I&amp;#8217;m sure she&amp;#8217;ll have &lt;a href=&quot;http://www.slideshare.net/stubbornella&quot;&gt;slides up soon,&lt;/a&gt; but here are some points that stood out to me:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Avoid selectors that target ID&amp;#8217;s. You&amp;#8217;re looking for visual patterns, not elements.&lt;/li&gt;
	&lt;li&gt;Avoid browser-targeting classes (e.g., .ie). Developers may not see these styles in Firebug, and may change the class it overrides in a way that breaks your site on the targeted browser.&lt;/li&gt;
	&lt;li&gt;Use grep to find patterns that have been overlooked:
	&lt;ul&gt;
		&lt;li&gt;Colors that are commonly declared often belong to the same visual pattern.&lt;/li&gt;
		&lt;li&gt;h1-h6 get overly declared. You should be able to merge these.&lt;/li&gt;
		&lt;li&gt;Lots of floats in the &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; is an indication that you need to use grids.&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nicole also has a very cool sounding alternative to the overflow:hidden technique, that I hope she&amp;#8217;ll blog about soon.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://twitter.com/sh1mmer&quot;&gt;@sh1mmer:&lt;/a&gt; JavaScript Everywhere! A 100% JavaScript Web Stack&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.slideshare.net/sh1mmer/txjs-4424188&quot;&gt;Slides from Tom&amp;#8217;s talk are online.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8220;Progressive enhancement is free.&amp;#8221;&lt;/p&gt;
&lt;p&gt;Just one of the exciting possibilities of Node is code re-use between the server and client. Tom talked about &lt;span class=&quot;caps&quot;&gt;YUI&lt;/span&gt; specifically, and why he feels that it&amp;#8217;s a particularly good candidate for code reuse since so many of its modularized components are useful outside the context of the &lt;span class=&quot;caps&quot;&gt;DOM&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;But since I have no previous experience with &lt;span class=&quot;caps&quot;&gt;YUI&lt;/span&gt; itself, Tom really caught my attention when he started talking about using Node to render &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; through &lt;span class=&quot;caps&quot;&gt;DOM&lt;/span&gt; methods via &lt;a href=&quot;http://github.com/tmpvar/jsdom&quot;&gt;jsdom&lt;/a&gt; and &lt;a href=&quot;http://github.com/tautologistics/node-htmlparser&quot;&gt;node-htmlparser,&lt;/a&gt; which Dav Glass supports in his &lt;a href=&quot;http://github.com/davglass/nodejs-yui3&quot;&gt;YUI3 bootstrapper for Node.&lt;/a&gt; As Tom said, this makes progressive enhancement good as free. He demonstrated the &lt;span class=&quot;caps&quot;&gt;YUI&lt;/span&gt; calendar control rendered by the server. With &lt;span class=&quot;caps&quot;&gt;SSJS&lt;/span&gt;, users could see and interact with the calendar before the page had even finished loading. Another possibility Tom mentioned would have Tom Mocchino excited: using JavaScript to pull only the data for page requests, and render it with templates; through progressive enhancement, the server could draw complete pages if requested by direct URL&amp;#8217;s.&lt;/p&gt;
&lt;h3&gt;@kangax: Dive into ECMAScript 5&lt;/h3&gt;
&lt;p&gt;Why, yes, it will be a while before we can safely use ES5 in all browsers. (To corrupt Crockford: &lt;a href=&quot;http://kangax.github.com/es5-compat-table/&quot;&gt;IE 6, 7, 8, and 9 &lt;span class=&quot;caps&quot;&gt;MUST&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;DIE&lt;/span&gt;.)&lt;/a&gt; But we&amp;#8217;ll see &lt;span class=&quot;caps&quot;&gt;JSC&lt;/span&gt; &amp;amp; V8 support long before then, and it pays to be aware of any nice or nasty surprises, like &lt;code&gt;arguments.callee&lt;/code&gt; being gone; yes, even &lt;code&gt;.caller&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, a confession: I haven&amp;#8217;t paid enough attention to ES5, resting on my laurels knowing it&amp;#8217;d be an age before I could use it on the front end. Unfortunately, a lot of Juriy&amp;#8217;s talk went over my head at the time. If you have slides of it, &lt;a href=&quot;http://twitter.com/?status=@jdbartlett+i+love+you+now+go+here&quot;&gt;please tweet me.&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;@phiggins: It&amp;#8217;s Just JavaScript&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.slideshare.net/phiggins/txjs&quot;&gt;Slides from Peter&amp;#8217;s talk are online.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I thought this would be &amp;#8220;10 Things I Learned from the Dojo Source&amp;#8221;, but Peter took a different approach entirely. A main message of his session was that playing with JavaScript is the fun path to grokking it. He took us through some common language patterns (curring &amp;amp; binding and variations thereof, etc.) in the context of a library he&amp;#8217;s been working as an experimental side-project. With a nod to Paul, he also covered a pattern for &amp;#8220;duck punching&amp;#8221; methods. These are conceptually complex patterns, and it&amp;#8217;s helpful to have practical examples of their application. I read the section on currying several times in The Good Parts, and never really understood &lt;em&gt;why&lt;/em&gt; I&amp;#8217;d want to use it. Helpful session.&lt;/p&gt;
&lt;h3&gt;@getify: Web Performance &amp;amp; UI Architecture&lt;/h3&gt;
&lt;p&gt;I wanna use Node nao! But our company&amp;#8217;s hugely invested in a Not-Node architecture. Among other things, Kyle presented a &lt;a href=&quot;http://www.slideshare.net/shadedecho/ui-architecture-web-performance/43&quot;&gt;&lt;span class=&quot;caps&quot;&gt;CVC&lt;/span&gt; (Client-View-Controller) architecture&lt;/a&gt; as it would apply to a mostly (or fully) JS web stack. Basically: the architectures we&amp;#8217;re already using are capable of kicking out &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt;, and many of us already build &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; API&amp;#8217;s for our apps. Why not reduce our apps to black boxes that read &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; requests from and return &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; to JavaScript-based UI controllers &amp;amp; views? That way, we&amp;#8217;ll still have the advantage of sharing the libraries we&amp;#8217;d want to share between the server and the client, and we&amp;#8217;d have a better separation between our application model and our views &amp;amp; controllers.&lt;/p&gt;
&lt;p&gt;Kyle also talked a bit about &lt;a href=&quot;http://labjs.com/&quot;&gt;LABjs&lt;/a&gt;, which I began using recently and plan to use for pretty much every project I start. Very useful to know that 2 or 3 .js files seems to be the sweet spot for parallel downloads.&lt;/p&gt;
&lt;h3&gt;Also&lt;/h3&gt;
&lt;p&gt;All this, and a Facebook sponsored before-party, a Media Temple sponsored open bar at lunch, and a Google sponsored &lt;code&gt;.party:after&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An understatement: I learned a lot from &lt;span class=&quot;caps&quot;&gt;TXJS&lt;/span&gt;. Equally valuably, I learned &lt;em&gt;about&lt;/em&gt; a lot &amp;#8212; things that&amp;#8217;d never before entered my sphere of awareness; and I can&amp;#8217;t learn about something until I know I don&amp;#8217;t know it. The speakers I spoke to were magnanimous and passionate, and I think there&amp;#8217;s something valuable too about being able to pin personalities to some of the tools and libraries I&amp;#8217;ve been using and articles and books I&amp;#8217;ve been reading these past few years. Aside from a couple of Bar Camp events, &lt;span class=&quot;caps&quot;&gt;TXJS&lt;/span&gt; was my first proper tech conference, and I came away from it with an overwhelmingly positive perspective. Thanks again, &lt;a href=&quot;http://twitter.com/rmurphey&quot;&gt;@rmurphey&lt;/a&gt; and the entire yayQuery team, and of course all the speakers and sponsors and attendees.&lt;/p&gt;</content>
		</entry>
	
		<entry>
			<title>BayesianAverage CakePHP plugin Performance Tests</title>
			<link href="http://jdbartlett.github.com/2009/12/05/bayesianaverage-cakephp-plugin-performance-tests/" />
			<id>http://jdbartlett.github.com/2009/12/05/bayesianaverage-cakephp-plugin-performance-tests/</id>
			<updated>2009-12-05T00:00:00-08:00</updated>
			<content type="html">&lt;p&gt;&lt;a href=&quot;http://github.com/jdbartlett/BayesianAverage&quot;&gt;The BayesianAverage CakePHP plugin&lt;/a&gt; was written with performance as a consideration and documented to help developers avoid performance problems. However, when the values of &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; are left for the behavior to calculate, two queries must be run that present a performance bottleneck.&lt;/p&gt;
&lt;p&gt;I decided to test the performance of these queries for medium-large data sets, so I&amp;#8217;d know what to expect using the plugin in a production environment.&lt;/p&gt;
&lt;h3&gt;Environment &amp;amp; Method&lt;/h3&gt;
&lt;p&gt;These tests were all done locally on K9, my MacBook Pro: 2.4 GHz Core 2 Duo, 2 GB &lt;span class=&quot;caps&quot;&gt;RAM&lt;/span&gt;, running MySQL Server 5.1.37.&lt;/p&gt;
&lt;p&gt;This is the basic structure of the items table:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
CREATE TABLE `items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `ratings_count` int(7) unsigned NOT NULL,
  `mean_rating` decimal(5,4) unsigned NOT NULL,
  `bayesian_rating` decimal(5,4) unsigned NOT NULL
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Neither of the &amp;#8220;problem&amp;#8221; queries examine the actual ratings table, so I won&amp;#8217;t go into its structure except to say that about 125 million ratings were spread across 50,000 items in my largest test data set.&lt;/p&gt;
&lt;p&gt;I decided to run my tests twice: against the basic table above (average row size: 25 bytes), and against a wider (but otherwise identical) table filled with random strings (average row size: 1.4 kilobytes). &lt;a href=&quot;http://drop.io/hidden/eskltizj8plodk/asset/YmF5ZXNpYW5hdmVyYWdlLWJlbmNobWFya3Mtc3FsLWd6&quot;&gt;You can download the test template for the wider table, complete with data, from my drop.io.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I ran eight tests on both tables (25 byte rows and 1.4 k rows) for both &amp;#8220;problem&amp;#8221; queries. I ran each test 10 times and recorded the average performance as well as the best and worst performance. For the rest of this post, I&amp;#8217;ll only discuss the average performance, but you can see the the other results &lt;a href=&quot;http://spreadsheets.google.com/ccc?key=0Akz7pCYdzld4dElqaTR3Z3NvcUZLck4xeG5ILXZMZ0E&amp;amp;hl=en&quot;&gt;in this Google Docs Spreadsheet.&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calculating &lt;em&gt;C&lt;/em&gt; &amp;amp; &lt;em&gt;m&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;The first &amp;#8220;problem&amp;#8221; query runs every half hour when someone adds or modifies a rating, and recalculates the values to cache for &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; if they aren&amp;#8217;t set explicitly in the model. If the recalculated values differ from the cache by more than 10%, the cache is replaced, otherwise the behavior keeps the cache and waits another half hour (and until someone adds or changes another vote) and tries again. This can be useful if you have a small, volatile data set (e.g., a review site that&amp;#8217;s only just launched), but as your data set grows, these values will become less volatile and you should consider making &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; explicit constants within your model to avoid performance problems.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SELECT AVG(`Item`.`ratings_count`) C, AVG(`Item`.`mean_rating`) m FROM `items` AS `Item` WHERE `Item`.`ratings_count` &amp;gt; 0 LIMIT 1;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s how the query performed with my test data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://spreadsheets.google.com/oimg?key=0Akz7pCYdzld4dElqaTR3Z3NvcUZLck4xeG5ILXZMZ0E&amp;amp;oid=4&amp;amp;v=1260034519014&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;tr&gt;
		&lt;td&gt;No of Items&lt;/td&gt;
		&lt;td&gt;Time w/25 byte rows&lt;/td&gt;
		&lt;td&gt;Time w/1.4 k rows&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;50k&lt;/td&gt;
		&lt;td&gt;41.8 ms&lt;/td&gt;
		&lt;td&gt;177.8 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;30k&lt;/td&gt;
		&lt;td&gt;29.92 ms&lt;/td&gt;
		&lt;td&gt;122.4 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;20k&lt;/td&gt;
		&lt;td&gt;26.07 ms&lt;/td&gt;
		&lt;td&gt;96.72 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;10k&lt;/td&gt;
		&lt;td&gt;18.97 ms&lt;/td&gt;
		&lt;td&gt;69.38 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;5k&lt;/td&gt;
		&lt;td&gt;13.58 ms&lt;/td&gt;
		&lt;td&gt;55.71 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;2.5k&lt;/td&gt;
		&lt;td&gt;13.77 ms&lt;/td&gt;
		&lt;td&gt;52.26 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;1k&lt;/td&gt;
		&lt;td&gt;12.43 ms&lt;/td&gt;
		&lt;td&gt;45.14 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;500&lt;/td&gt;
		&lt;td&gt;11.69 ms&lt;/td&gt;
		&lt;td&gt;43.1 ms&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;As an experiment, I tried creating an index on the relevant fields (&lt;code&gt;KEY `ratings_count` (`ratings_count`,`mean_rating`)&lt;/code&gt;), which reduced query time by more than 100 ms for the wider table at 50,000 rows.&lt;/p&gt;
&lt;h3&gt;Recalculating Items&lt;/h3&gt;
&lt;p&gt;The second query that could present an even more serious performance bottleneck updates the &lt;code&gt;bayesian_rating&lt;/code&gt; for all items when the cache values for &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; are updated or created. Again, this query never runs if &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; are set manually, and only runs after the cache is updated or created. As your data set grows, &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; will become less volatile, meaning recalculated values will differ from the cache by less than 10%, and this query is unlikely to run.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;UPDATE `items` AS `Item` SET `Item`.`bayesian_rating` = (`Item`.`ratings_count` / (`Item`.`ratings_count` + 2518.8187)) * `Item`.`mean_rating` + (2518.8187 / (`Item`.`ratings_count` + 2518.8187)) * 2.99895026 WHERE `Item`.`ratings_count` &amp;gt; 0;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s how the query performed with my test data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://spreadsheets.google.com/oimg?key=0Akz7pCYdzld4dElqaTR3Z3NvcUZLck4xeG5ILXZMZ0E&amp;amp;oid=6&amp;amp;v=1260036053124&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;tr&gt;
		&lt;td&gt;No of Items&lt;/td&gt;
		&lt;td&gt;Time w/25 byte rows&lt;/td&gt;
		&lt;td&gt;Time w/1.4 k rows&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;50k&lt;/td&gt;
		&lt;td&gt;220.7 ms&lt;/td&gt;
		&lt;td&gt;966.6 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;30k&lt;/td&gt;
		&lt;td&gt;138 ms&lt;/td&gt;
		&lt;td&gt;680.7 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;20k&lt;/td&gt;
		&lt;td&gt;94.38 ms&lt;/td&gt;
		&lt;td&gt;539 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;10k&lt;/td&gt;
		&lt;td&gt;52.64 ms&lt;/td&gt;
		&lt;td&gt;407.3 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;5k&lt;/td&gt;
		&lt;td&gt;33.81 ms&lt;/td&gt;
		&lt;td&gt;334.7 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;2.5k&lt;/td&gt;
		&lt;td&gt;22.92 ms&lt;/td&gt;
		&lt;td&gt;297.7 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;1k&lt;/td&gt;
		&lt;td&gt;15.56 ms&lt;/td&gt;
		&lt;td&gt;276.8 ms&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;500&lt;/td&gt;
		&lt;td&gt;12.51 ms&lt;/td&gt;
		&lt;td&gt;269 ms&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
&lt;h3&gt;Analysis &amp;amp; Recommendations&lt;/h3&gt;
&lt;p&gt;In a case where all bayesian ratings have to be recalculated, both the select and update queries must be run in the same request. With the longest, widest data set I tested (50,000 items; 1.4 k per item), the two queries together took just over a second to complete in a MyISAM table. I think that&amp;#8217;s unacceptable given the environment; in fact, all the results for the 1.4 k/row table made me cringe a little. That said, the second query would run extremely rarely &amp;#8212; probably never &amp;#8212; for a long items table. The longer the list of voted items, the less likely it is you&amp;#8217;ll have to recalculate all Bayesian averages.&lt;/p&gt;
&lt;p&gt;These are my recommendations:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Once you&amp;#8217;ve set &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; within your model, these performance concerns no longer apply. Set them when you can. If your results look wrong after setting them, you can remove the setting and the data will fix itself next time a vote is added. &lt;strong&gt;&lt;em&gt;C&lt;/em&gt; is the average number of votes you expect per item, and &lt;em&gt;m&lt;/em&gt; is the average rating an item is given&lt;/strong&gt; (the median rating works in a pinch; e.g., &amp;#8220;3&amp;#8221; for a 5-star rating).&lt;/li&gt;
	&lt;li&gt;Create a single index for the &lt;em&gt;two&lt;/em&gt; fields &lt;code&gt;ratings_count&lt;/code&gt; and &lt;code&gt;mean_rating&lt;/code&gt;. I found this improved the calculation of &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; dramatically.&lt;/li&gt;
	&lt;li&gt;If you have a wide &amp;#8220;items&amp;#8221; table and want to use BayesianAverageable in &amp;#8220;magic&amp;#8221; mode, you may want to do some normalization for the sake of performance.&lt;/li&gt;
	&lt;li&gt;If you have a wide &amp;#8220;items&amp;#8221; table and normalization isn&amp;#8217;t an option, you may want to separate the &lt;code&gt;ratings_count&lt;/code&gt;, &lt;code&gt;mean_rating&lt;/code&gt;, and &lt;code&gt;bayesian_rating&lt;/code&gt; fields into an &lt;code&gt;item_averages&lt;/code&gt; table and add &lt;code&gt;'itemModel' =&amp;gt; 'ItemAverage'&lt;/code&gt; to BayesianAverageable&amp;#8217;s settings from your model.&lt;/li&gt;
	&lt;li&gt;Make sure caching is &lt;em&gt;not&lt;/em&gt; disabled on your production environment. (The line &lt;code&gt;Configure::write('Cache.disable', true);&lt;/code&gt; should be commented out in &lt;code&gt;/app/config/core.php&lt;/code&gt;)&lt;/li&gt;
	&lt;li&gt;Set up a dedicated cache configuration to make sure the cache for &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; isn&amp;#8217;t deleted.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A how-to for that last recommendation: CakePHP&amp;#8217;s default cache configuration is set to expire after an hour. To add another cache configuration, open &lt;code&gt;/app/config/core.php&lt;/code&gt; and add the following:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
Cache::config('annual', array(
  'engine' =&amp;gt; 'File',
  'duration'=&amp;gt; '+1 year',
));
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Tell BayesianAverageable to use this Cache configuration instead of the default through the &amp;#8216;&lt;em&gt;cache&lt;/em&gt;&amp;#8217; setting:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
var $actsAs = array('BayesianAverage.BayesianAverageable' =&amp;gt; array(
  'fields' =&amp;gt; array('itemId' =&amp;gt; 'item_id'),
  'cache' =&amp;gt; array(       // &amp;lt;=
    'config' =&amp;gt; 'annual', // &amp;lt;= lookit!
  ),                      // &amp;lt;=
));
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This should stop the cache file containing &lt;em&gt;C&lt;/em&gt; and &lt;em&gt;m&lt;/em&gt; from expiring, further decreasing the chances you&amp;#8217;ll have to recalculate them.&lt;/p&gt;
&lt;p&gt;For my part, I&amp;#8217;m going to modify the way the &amp;#8220;items&amp;#8221; table is updated so that the query is either given a &lt;code&gt;LOW_PRIORITY&lt;/code&gt; or run in batches so that select operations can continue. I&amp;#8217;m also going to make it easier to delegate the &amp;#8220;problem&amp;#8221; queries to a shell task so they don&amp;#8217;t delay a user&amp;#8217;s request. Finally, I&amp;#8217;ll update the plugin&amp;#8217;s documentation to reflect the above recommendations.&lt;/p&gt;</content>
		</entry>
	
		<entry>
			<title>Working with Time Zones in CakePHP</title>
			<link href="http://jdbartlett.github.com/2009/08/27/cakephp-time-zones/" />
			<id>http://jdbartlett.github.com/2009/08/27/cakephp-time-zones/</id>
			<updated>2009-08-27T00:00:00-07:00</updated>
			<content type="html">&lt;p&gt;&lt;strong&gt;Rule #1 of working with time zones&lt;/strong&gt; is: don&amp;#8217;t have users pick abbreviations like &amp;#8220;&lt;span class=&quot;caps&quot;&gt;GMT&lt;/span&gt;&amp;#8221;. Instead, have them select their &lt;i&gt;location&lt;/i&gt; or zoneinfo zone name. Abbreviated time zones are fickle. Look at England for example: its abbreviated time zone is currently &amp;#8220;&lt;span class=&quot;caps&quot;&gt;BST&lt;/span&gt;&amp;#8221;, and in a few months it&amp;#8217;ll be &amp;#8220;&lt;span class=&quot;caps&quot;&gt;GMT&lt;/span&gt;&amp;#8221;, but its zone name is always &lt;code&gt;Europe/London&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://gist.github.com/174907&quot;&gt;Here&amp;#8217;s a helper&lt;/a&gt; to produce a &lt;code&gt;$form-&amp;gt;input&lt;/code&gt;able array of times for various locations in the pattern &amp;#8220;4:20 PM &amp;#8211; New York City, United States&amp;#8221;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TimezonesHelper&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppHelper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt; 	
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; 	&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; 		&lt;span class=&quot;nv&quot;&gt;$zones&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Pacific/Apia&amp;#39;&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Apia, Upolu, Samoa&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                      &lt;span class=&quot;c1&quot;&gt;// UTC-11:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Hawaii&amp;#39;&lt;/span&gt;                &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Honolulu, Oahu, Hawaii, United States&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// UTC-10:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Alaska&amp;#39;&lt;/span&gt;                &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Anchorage, Alaska, United States&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// UTC-09:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Pacific&amp;#39;&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Los Angeles, California, United States&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// UTC-08:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Mountain&amp;#39;&lt;/span&gt;              &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Phoenix, Arizona, United States&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;// UTC-07:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Central&amp;#39;&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Chicago, Illinois, United States&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// UTC-06:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Eastern&amp;#39;&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;New York City, United States&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// UTC-05:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;America/Santiago&amp;#39;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Santiago, Chile&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                         &lt;span class=&quot;c1&quot;&gt;// UTC-04:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;America/Sao_Paulo&amp;#39;&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;São Paulo, Brazil&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;// UTC-03:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Atlantic/South_Georgia&amp;#39;&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;South Georgia, S. Sandwich Islands&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// UTC-02:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Atlantic/Cape_Verde&amp;#39;&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Praia, Cape Verde&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;// UTC-01:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Europe/London&amp;#39;&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;London, United Kingdom&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;// UTC+00:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;UTC&amp;#39;&lt;/span&gt;                      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Universal Coordinated Time (UTC)&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// UTC+00:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Europe/Paris&amp;#39;&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Paris, France&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                           &lt;span class=&quot;c1&quot;&gt;// UTC+01:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Africa/Cairo&amp;#39;&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Cairo, Egypt&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                            &lt;span class=&quot;c1&quot;&gt;// UTC+02:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Europe/Moscow&amp;#39;&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Moscow, Russia&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;// UTC+03:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Asia/Dubai&amp;#39;&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Dubai, United Arab Emirates&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;// UTC+04:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Asia/Karachi&amp;#39;&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Karachi, Pakistan&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;// UTC+05:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Asia/Dhaka&amp;#39;&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Dhaka, Bangladesh&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;// UTC+06:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Asia/Jakarta&amp;#39;&lt;/span&gt;             &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Jakarta, Indonesia&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                      &lt;span class=&quot;c1&quot;&gt;// UTC+07:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Asia/Hong_Kong&amp;#39;&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hong Kong, China&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;// UTC+08:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Asia/Tokyo&amp;#39;&lt;/span&gt;               &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Tokyo, Japan&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                            &lt;span class=&quot;c1&quot;&gt;// UTC+09:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Australia/Sydney&amp;#39;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Sydney, Australia&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;// UTC+10:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29&lt;/span&gt; 			&lt;span class=&quot;s1&quot;&gt;&amp;#39;Pacific/Noumea&amp;#39;&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Nouméa, New Caledonia, France&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;// UTC+11:00&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30&lt;/span&gt; 		&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31&lt;/span&gt; 		&lt;span class=&quot;nv&quot;&gt;$dateTime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;now&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32&lt;/span&gt; 		&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zones&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33&lt;/span&gt; 			&lt;span class=&quot;nv&quot;&gt;$zoneObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DateTimeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34&lt;/span&gt; 			&lt;span class=&quot;nv&quot;&gt;$dateTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setTimezone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zoneObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35&lt;/span&gt; 			&lt;span class=&quot;nv&quot;&gt;$zones&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$zone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dateTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;g:i A - &amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36&lt;/span&gt; 		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;37&lt;/span&gt; 		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$zones&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38&lt;/span&gt; 	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39&lt;/span&gt; 	
&lt;span class=&quot;lineno&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Call it from your view like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;time_zone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt; 	&lt;span class=&quot;s1&quot;&gt;&amp;#39;options&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$timezones&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;3&lt;/span&gt; 	&lt;span class=&quot;s1&quot;&gt;&amp;#39;default&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;US/Eastern&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Rule #2&lt;/strong&gt; is have your entire app and database work from one time zone, applying offsets only when interacting with users (e.g., in view files). You can go with your server&amp;#8217;s default time zone, but if you&amp;#8217;d like to secure your app&amp;#8217;s use of &lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt;, follow these steps:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Getting the app itself to use &lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt; is easy: add the line &lt;code&gt;date_default_timezone_set('UTC');&lt;/code&gt; toward the top of /app/cofig/bootstrap.php.&lt;/li&gt;
	&lt;li&gt;Having MySQL report dates in &lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt;&amp;#8212;and if you followed the first step, this one is important when you &lt;code&gt;SELECT NOW()&lt;/code&gt; or do any date math&amp;#8212;is a little more involved. I did it by copying /cake/libs/model/datasources/dbo/dbo_mysql.php to /app/models/datasources/dbo/dbo_mysql.php and adding the line &lt;code&gt;$this-&amp;gt;_execute(&quot;SET time_zone = '+00:00'&quot;);&lt;/code&gt; to DboMysql::connect, just after it sets the encoding (line 387 in the current stable version of CakePHP 1.2). I was tempted to run the query directly from AppModel instead, but I&amp;#8217;m not sure how robust that solution would be.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Again, the above steps are optional. Whatever time zone your app uses, you&amp;#8217;ll need the current user&amp;#8217;s &lt;i&gt;&lt;span class=&quot;caps&quot;&gt;GMT&lt;/span&gt;/&lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt;&lt;/i&gt; offset for the &amp;#8220;userOffset&amp;#8221; parameter &lt;a href=&quot;http://api.cakephp.org/file/libs/view/helpers/time.php#method-TimeHelpertimeAgoInWords&quot;&gt;in CakePHP&amp;#8217;s Time helper&lt;/a&gt;. You may be tempted to calculate the value of this offset and store it in the database for each user, but remember daylight savings: &lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt; offsets aren&amp;#8217;t static, so generate them in an afterFind (retrieve) instead of a beforeSave (create, update):&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppModel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; 	&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;afterFind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; 		&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$results&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt; 			&lt;span class=&quot;nv&quot;&gt;$user_zone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DateTimeZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;User&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;time_zone&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt; 			&lt;span class=&quot;nv&quot;&gt;$user_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;now&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user_zone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt; 			&lt;span class=&quot;nv&quot;&gt;$results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;User&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;utc_offset&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$user_zone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOffset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$user_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt; 		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; 		
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt; 		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt; 	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you use AuthComponent and the above afterFind, the current user&amp;#8217;s &lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt; offset will be stored in &lt;code&gt;Session.Auth.User.utc_offset&lt;/code&gt;, so you can easily apply its inverse before handling user input.&lt;/p&gt;
&lt;p&gt;The above methods sacrifice performance for versatility and reliability. For scalability, you may want to cache &lt;span class=&quot;caps&quot;&gt;UTC&lt;/span&gt; offsets, perhaps directly in the user table. But some variation on the above will give you the greatest amount of accuracy for the greatest number of time zones.&lt;/p&gt;</content>
		</entry>
	
		<entry>
			<title>YourMom hasAndBelongsToMany</title>
			<link href="http://jdbartlett.github.com/2008/07/28/yourmom-hasandbelongstomany/" />
			<id>http://jdbartlett.github.com/2008/07/28/yourmom-hasandbelongstomany/</id>
			<updated>2008-07-28T00:00:00-07:00</updated>
			<content type="html">&lt;p&gt;One of my first tasks in CakePHP was to produce a paginated list of blog posts with each post&amp;#8217;s tags on the same page. A straightforward couple of &lt;span class=&quot;caps&quot;&gt;SQL&lt;/span&gt; queries would&amp;#8217;ve done the job nicely, I kept reminding myself over the many hours wasted stumbling through poorly-structured documentation as I missed milestone deadlines. I was doing something very wrong.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Wu_Wei&quot;&gt;Wu Wei&lt;/a&gt; is very important when you start off with a new framework. Your preconceptions will slow you down, especially if you&amp;#8217;re very experienced with the framework&amp;#8217;s language, or you&amp;#8217;ve enjoyed another framework in the same language. When you find yourself thinking something as popular as CakePHP was made wrong or its behavior fails some expectation, the real problem is probably a prejudice on your part. Those carefree days of CodeIgniter, that Golden Age of frameworkless ignorance, are leading you to think Cake should act according to your expectations. Abandon that preconception and suddenly you&amp;#8217;re free to swim with the flow of your framework instead of against it. Yes, in another metaphor I&amp;#8217;d be asking you to drink the Kool-Aid, but not all Kool-Aid is poisoned/grape flavor. (Note: grape flavor is evil.)&lt;/p&gt;
&lt;p&gt;The answer to my proximate problem wasn&amp;#8217;t really in the manual. I didn&amp;#8217;t find it in any other blogs, either. I found it by ignoring my expectations and paying attention to what I was writing in context of Cake, not &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s what I added to my PostsController::index:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PostsTag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bindModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;3&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;belongsTo&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PostsTag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;6&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;recursive&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;7&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;group&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;PostsTag.post_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The relationship between PostsTag and Post doesn&amp;#8217;t occur automatically, it must be stated explicitly. A little recursion then brings in all the missing post tags. If a Post has more than one tag, you&amp;#8217;ll see it duplicated accordingly in your results set; easily solved with a little GROUPing. I&amp;#8217;m doing straight &lt;code&gt;find()&lt;/code&gt;s for these examples, but Cake makes it trivial to add pagination.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you want to do the same thing&amp;#8212;produce a list of Posts with Tags for each post&amp;#8212;but you only want to display those Posts which have a certain tag. You can do pretty much the same thing, adding Tag to your bindings:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PostsTag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bindModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;belongsTo&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Tag&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PostsTag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;conditions&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Tag.name&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;CakePHP&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;recursive&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;group&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;PostsTag.post_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You&amp;#8217;ll note I&amp;#8217;ve kept the &lt;span class=&quot;caps&quot;&gt;GROUP&lt;/span&gt; in there. It&amp;#8217;s unlikely you&amp;#8217;ll allow users to give posts the same tag twice, but you may want to specify an OR condition, for example, in a simple tag-or-title search:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PostsTag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bindModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;belongsTo&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Post&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Tag&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PostsTag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;conditions&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;         &lt;span class=&quot;s1&quot;&gt;&amp;#39;OR&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;             &lt;span class=&quot;s1&quot;&gt;&amp;#39;Tag.name&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;CakePHP&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;             &lt;span class=&quot;s1&quot;&gt;&amp;#39;Post.title LIKE&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;%CakePHP%&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;recursive&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt;     &lt;span class=&quot;s1&quot;&gt;&amp;#39;group&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;PostsTag.post_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;One final note: if you&amp;#8217;d like to avoid the constipation of binding directly from the controller, &lt;a href=&quot;http://bakery.cakephp.org/articles/view/keeping-bindmodel-and-unbindmodel-out-of-your-controllers&quot;&gt;set up an &lt;code&gt;expects()&lt;/code&gt; method&lt;/a&gt; within the PostsTag model, which you&amp;#8217;ll have to create.&lt;/p&gt;
&lt;p&gt;By the time I realized all this, I&amp;#8217;d settled for a clunky, straight-&lt;span class=&quot;caps&quot;&gt;SQL&lt;/span&gt; solution that&amp;#8217;s already causing all sorts of unnecessary condition-checking in my view. While I&amp;#8217;ve been swimming against the flow of the framework, my poop has been floating downstream and piling up under the sensitive noses of industrious, dam-building beavers. If I&amp;#8217;d stopped thinking in terms of my existing skillset and instead become the uncarved block, I could be enjoying a delicious slice of Cake instead of fishing turds out my code. To paraphrase the ancient Taoist philosopher, Cypher:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You know, I know this cake doesn&amp;#8217;t exist. I know that when I put it in my mouth, the Matrix is telling my brain it is icing-coated and delicious. Also, its documentation is very well-structured and explains things quite thoroughly without any vague or contradictory statements. Er, where was I?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PS: CakePHP is made of candy.&lt;/p&gt;</content>
		</entry>
	
		<entry>
			<title>Tree Drawing with the Adjacency List Model</title>
			<link href="http://jdbartlett.github.com/2008/01/15/tree-drawing-with-the-adjacency-list-model/" />
			<id>http://jdbartlett.github.com/2008/01/15/tree-drawing-with-the-adjacency-list-model/</id>
			<updated>2008-01-15T00:00:00-08:00</updated>
			<content type="html">&lt;p&gt;Let&amp;#8217;s not fake piety, we&amp;#8217;ve all of us stuffed a few hierarchies into the adjacency list model. It&amp;#8217;s so easy to add &lt;code&gt;parent_id&lt;/code&gt; to the end of any table, and the performance issues seem negligible in small data sets. It&amp;#8217;s only when we&amp;#8217;re grabbing data to draw the hierarchical tree it represents that we maybe worry about recursion&amp;#8217;s inherent performance drawbacks. For example, you&amp;#8217;ve probably seen a method like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysql_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;SELECT id&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;        FROM family&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;        WHERE parent=&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$parent&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;mysql_fetch_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;         &lt;span class=&quot;nv&quot;&gt;$children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;get_children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;One obvious problem is that this function runs a query for every single node it discovers. Another is that the amount of recursion this function produces depends, not on the size of your data set, but on the complexity of the hierarchy your data represents; the more branches your tree has, the deeper its recursion goes. Even if you have a small number of rows such that preorder tree traversal seems overkill, the complexity of your tree may still demand a more efficient artist than &lt;code&gt;get_children&lt;/code&gt;. I&amp;#8217;d like to propose one alternative for producing a complete tree.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s my data: two families of the animal kingdom:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Hapalemurinae
	&lt;ul&gt;
		&lt;li&gt;Hapalemur
		&lt;ul&gt;
			&lt;li&gt;Hapalemur Alaotrensis&lt;/li&gt;
			&lt;li&gt;Hapalemur Aureus&lt;/li&gt;
			&lt;li&gt;Hapalemur Griseus&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
		&lt;li&gt;Prolemur
		&lt;ul&gt;
			&lt;li&gt;Prolemur Simus&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;Lemurinae
	&lt;ul&gt;
		&lt;li&gt;Eulemur
		&lt;ul&gt;
			&lt;li&gt;Eulemur Coronatus&lt;/li&gt;
			&lt;li&gt;Eulemur Fulvus&lt;/li&gt;
			&lt;li&gt;Eulemur Macaco&lt;/li&gt;
			&lt;li&gt;Eulemur Mongoz&lt;/li&gt;
			&lt;li&gt;Eulemur Rubriventer&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
		&lt;li&gt;Lemur
		&lt;ul&gt;
			&lt;li&gt;Lemur Catta&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
		&lt;li&gt;Varecia
		&lt;ul&gt;
			&lt;li&gt;Varecia Rubra&lt;/li&gt;
			&lt;li&gt;Varecia Variegata&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To help emphasize the concept I&amp;#8217;m promoting, I&amp;#8217;m using taxonomic names as IDs and parents in the &lt;code&gt;$family&lt;/code&gt; array I&amp;#8217;m using to represent this data:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$family&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur Coronatus&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur Fulvus&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur Macaco&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur Mongoz&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur Rubriventer&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Eulemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur Alaotrensis&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur Aureus&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur Griseus&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemur Catta&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Prolemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Hapalemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Prolemur Simus&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Prolemur&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Varecia&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lemurinae&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Varecia Rubra&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Varecia&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Varecia Variegata&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;parent&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Varecia&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This still comes quite close to the result you might expect from a MySQL query.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the recursion-free method I&amp;#8217;m proposing:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tree&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$family&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;         &lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt;             &lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt;         &lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The results are stored in two arrays: &lt;code&gt;$branches&lt;/code&gt;, which contains individual branches of the hierarchy indexed by parent; and &lt;code&gt;$tree&lt;/code&gt;, which collects all branches in the context of a complete tree. &lt;code&gt;print_r($tree)&lt;/code&gt; and you&amp;#8217;ll see the whole structure; &lt;code&gt;print_r($branches['Hapalemurinae'])&lt;/code&gt; and you&amp;#8217;ll see only the children of Hapalemurinae.&lt;/p&gt;
&lt;p&gt;In the real world, this code would be more complex&amp;#8212;each node probably having more properties than its ID&amp;#8212;but the underlying principle of assigning nodes to the tree by reference can be used to create a relatively inexpensive &lt;code&gt;serialize()&lt;/code&gt;d tree-caching routine for small, complex data structures.&lt;/p&gt;</content>
		</entry>
	

</feed>
