Gali maždaug šitaip, jeigu nenori naudot rekursijos duombazės lygyje (čia naudoju native mysql, geriau naudot kokį wrapperį):
<?php
$construct_tree = function(&$result, $cats, $relations_cats, $ids, $depth) use (&$construct_tree) {
	if (is_array($ids) && count($ids) > 0):
		// iteruojam per visus paduotus id (tariamai vaikinius ID einamojo elemento)
		foreach($ids as $id):
			if (!isset($cats[$id])) {
				$result = array();
				continue;
			}
			$current_cat = $cats[$id]; // randam einamaja kategorija
			
			// uzsetinam einamosios kategorijos duomenis i rezultatu masyva
			$result['id'] = $current_cat['id'];
			$result['name'] = $current_cat['name'];
			$result['depth'] = $depth;
			$result['children'] = array();
			
			if (isset($relations_cats[$id])): // jeigu einamasis elementas turi vaiku - iskvieciam patys save, 
											  // tuos pacius veiksmus padarom kiekvienam is vaiku
				$children = $cats[$id];
				$construct_tree($result['children'], $cats, $relations_cats, $children, $depth+1);
			endif;
		endforeach;
	endif;
};
$result = mysql_query("SELECT id, parent_id, name, level FROM categories");
// suvaliduoji $result
$cats = array(); // masyvas, kuriame saugomos kategorijos. Raktas - kategorijos ID
$relations_cats = array(); // cia saugosim pseudo hierarchine struktura, kur elemento raktas - tevinis ID, o elementas - pats kategorijos ID
while ($row = mysql_fetch_assoc($result)):
	$cats[$row['id']] = $row;
	$relations_cats[$row['parent_id']][] = $row['id'];
endwhile;
$kategoriju_struktura = array();
$construct_tree($kategoriju_struktura, $cats, $relations_cats, array(0), 0); // pradedam nuo 0 tevo (nuo elementu, neturinciu tevo)
print_r($kategoriju_struktura);
/*
	Array(
		0 => Array(
			'id' => 1,
			'name' => 'Super kategorija 1',
			'depth' => 0,
			'children' => Array(
				0 => Array (
					'id' => 3,
					'name' => 'Subkategorija 2',
					'depth' => 1,
					'children' => Array( )
				),
				1 => Array (
					'id' => 4,
					'name' => 'Subkategorija 3',
					'depth' => 1,
					'children' => Array( )
				)
			)
		),
		1 => Array(
			'id' => 5,
			'name' => 'Super kategorija 2',
			'depth' => 0,
			'children' => Array( )
		)
	)
*/
print json_encode($kategoriju_struktura); // isvedam json'a
?>
Gal iš pradžių bus sunku kodą suprast, bet pasigilink. Klausk, jeigu kažkas tiksliai neaišku.