A PHP class to build parent child arrays from a flat array or database query results.

I am currently developing using Kohana 3.2 PHP Framework incorporating Mustache Templates and the Twitter Bootstrap with LESS CSS and loving it. I recently wrote this simple php class to convert a flat array with id and parent_id keys into a multi-dimensional array where parents have a `children` key with children in a sub-array.

class BuildParentChild {

	private $data = array();
	public $rendered;

	public function __construct(&$Input)
	{
		foreach($Input as $Item)
		{
			$Item= (array) $Item;
			$this->data['items'][$Item['id']] = $Item;
			$this->data['parents'][$Item['parent_id']][] = $Item['id'];
			if(!isset($this->top_level) || $this->top_level > $Item['parent_id'])
			{
				$this->top_level = $Item['parent_id'];
			}
		}
		return $this;
	}

	public function build($id)
	{
		$return{$id} = array();
		foreach($this->data['parents'][$id] as $child)
		{
			$build = $this->data['items'][$child];
			if(isset($this->data['parents'][$child]))
			{
				$build['has_children'] = true;
				$build['children'] = $this->build($child);
			}
			else
			{
				$build['has_children'] = false;
			}
			$return{$id}[] = $build;
		}
		return (array) $return{$id};
	}

	public function render()
	{
		if(!isset($this->rendered) || !is_array($this->rendered))
		{
			$this->rendered = $this->build($this->top_level);
		}
		return $this->rendered;
	}
}

$Menu = new BuildParentChild($menu_items);
var_dump($Menu->render());

If you put in rows like
id – parent_id – name
1 – 0 – Top
2 – 1 – Second Level
3 – 2 – Third Level
4 – 1 – Another Second Level
5 – 0 – Another Top Level

You would recieve

array(
  'id' => 1,
  'name' => top,
  'has_children' => true,
  'children' => array(
    array(
      'id' => 2,
      'name' => Second Level,
      'has_children' => true,

and so on.

Posted in Web & Database Design | Leave a comment

Using Child Themes with Artisteer sidebar templates.

I have been using artisteer to create the basic templates for some of my sites. With the release of WordPress 3.0 came many features. One that i like and have been learning to use is Child Themes. These are great for artisteer themes as i can make many changes to the child theme and if i update the parent theme the childs changes are carried over automatically. for more info on this check out the WP Codex or one of the many tutorials available on google. The problem with this is that artisteers templates need a quick change to make them compatible.

I found this problem when trying to use sidebar1.php in my child theme to replace the parents sidebar. The problem lies in how the artisteer template calls the sidebars. It uses the WP “TEMPLATEPATH” to load the php scripts. When using a child theme this creates a problem as TEMPLATEPATH points to the parent themes directory. To get sidebars to load from the current theme(child theme if selected) you need to search for TEMPLATEPATH in all your parent template files and replace it with STYLESHEETPATH which points to the directory of the current themes CSS stylesheet.

Posted in Artisteer | Tagged , , , , , , , , , | Leave a comment

Single Query Dynamic Multi-level Menu

Since writing Dynamic Multi-level CSS Menu with PHP and MySQL. SEO Ready I have learned quite a lot and in doing so found a much more efficient way of building this menu. This method varies in that it only makes one query to the menu table and compiles the results into a multidimensional array. The basic recurring function was just about the same, just taking into account the changes in data structure. Lets start with the query and array.

// Select all entries from the menu table
$result=mysql_query("SELECT id, label, link, parent FROM menu ORDER BY parent, sort, label");
// Create a multidimensional array to conatin a list of items and parents
$menu = array(
    'items' => array(),
    'parents' => array()
);
// Builds the array lists with data from the menu table
while ($items = mysql_fetch_assoc($result))
{
    // Creates entry into items array with current menu item id ie. $menu['items'][1]
    $menu['items'][$items['id']] = $items;
    // Creates entry into parents array. Parents array contains a list of all items with children
    $menu['parents'][$items['parent']][] = $items['id'];
}

The $menu contains 2 other arrays, items holds every result from the menu table query, the parents array holds a list of all item ids that have children. Next we use a while statement to run through the sql results and assign items to the arrays. If the items parent id already exists in the parents array it will be overwritten so there will only be 1 of each parent id listed.

// Menu builder function, parentId 0 is the root
function buildMenu($parent, $menu)
{
   $html = "";
   if (isset($menu['parents'][$parent]))
   {
      $html .= "
      <ul>\n";
       foreach ($menu['parents'][$parent] as $itemId)
       {
          if(!isset($menu['parents'][$itemId]))
          {
             $html .= "<li>\n  <a href='".$menu['items'][$itemId]['link']."'>".$menu['items'][$itemId]['label']."</a>\n</li> \n";
          }
          if(isset($menu['parents'][$itemId]))
          {
             $html .= "
             <li>\n  <a href='".$menu['items'][$itemId]['link']."'>".$menu['items'][$itemId]['label']."</a> \n";
             $html .= buildMenu($itemId, $menu);
             $html .= "</li> \n";
          }
       }
       $html .= "</ul> \n";
   }
   return $html;
}
echo buildMenu(0, $menu);

This version signifigantly reduces the strain on your server if you have hundreds or thousands of pages and still allows you to keep a completely dynamic menu.

Posted in Web & Database Design | Tagged , , , , , , , , , | 28 Comments

Ebay style search menu with category totals count displayed.

Image representing eBay as depicted in CrunchBase

Image via CrunchBase

I spent a few days lately working on a personal project and couldn’t get my search menu to function quite like i wanted. After several attempts with forms and such i wanted a less messy and more attractive search. After looking on eBay for something i realized that a similar search would work great for my needs and look good as well. Not only did it list all valid categories and options but it gave a total of the items listed for that category. After a few searches and a few attempts I got it working. Lets start by creating some demo data to work with, we will be using the below for this.

Click here for a working demo

The files we will be working with can be downloaded at the bottom of the page. The sql for this data is included.

id name color shape
1 Shape 1 red square
2 Shape 2 blue triangle
3 Shape 3 green circle
4 Shape 4 red triangle
5 Shape 5 blue circle

Now that we have some data to work with lets get started with the PHP that will create this search menu. Assuming you already have a MySQL connection made in you PHP document we will start by defining our 2 sql queries. The first query is used to get the count of all the items in each category (i.e. how many shapes are red, how many are blue, how many are green etc) and is done by using the Count and If statements to get our data.

The following will get a count of how many items are red and setting the results as reds:

$sql = "SELECT Count(If((demo.color = 'red'),1,NULL)) AS reds FROM demo";
$query = mysql_fetch_array(mysql_query($sql));
echo "Red(".$query['reds'].")<br/>";

The output of this should be “Red(2)”. We will later turn this into a link. Using this same Count statement we will form a much more advanced string for all of our options and later we will add a conditional WHERE clause to only get the results of options that we have already chosen. After adding the other option counts you should have something like this:

$count_sql = "SELECT
 Count(If((demo.color = 'red'),1,NULL)) AS reds,
 Count(If((demo.color = 'blue'),1,NULL)) AS blues,
 Count(If((demo.color = 'green'),1,NULL)) AS greens,
 Count(If((demo.shape = 'square'),1,NULL)) AS squares,
 Count(If((demo.shape = 'triangle'),1,NULL)) AS triangles,
 Count(If((demo.shape = 'circle'),1,NULL)) AS circles
FROM demo";
$view_sql = "SELECT * FROM demo";

As you can see the the first variable $count_sql uses our counts to get the amount of items in each category, when we add a WHERE statement it will only show the results that match our search conditions. The second $view_sql is used to select the items that match the search for viewing. On to the next section:

if(isset($_GET['clear'])){
   $clear = $_GET['clear'];
   unset($_GET[$clear]);
}

This will simply clear the variable for a specific option. This is used so that a user can remove a previously selected option from the results. The following code then checks for the existence of any chosen options and if they exist it then adds the appropriate WHERE condition to both of the sql variables. The $c variable is used to determine whether or not the AND statement is needed. The $link variable is used to set the full links for the options links below.

if(isset($_GET['color']) || isset($_GET['shape'])){
   $c = FALSE;
   $count_sql .= " WHERE";
   $view_sql .= " WHERE";
   $link = '?';
   if(isset($_GET['color'])){
      if($c == FALSE){
         $count_sql .= " color='".$_GET['color']."'";
         $view_sql .= " color='".$_GET['color']."'";
         $link .= "color=".$_GET['color'];
         $c = TRUE;
      } else {
         $count_sql .= " AND color='".$_GET['color']."'";
         $view_sql .= " AND color='".$_GET['color']."'";
         $link .= "&color=".$_GET['color'];
      }
   }
   if(isset($_GET['shape'])){
      if($c == FALSE){
         $count_sql .= " shape='".$_GET['shape']."'";
         $view_sql .= " shape='".$_GET['shape']."'";
         $link .= "shape=".$_GET['shape'];
         $c = TRUE;
      } else {
         $count_sql .= " AND shape='".$_GET['shape']."'";
         $view_sql .= " AND shape='".$_GET['shape']."'";
         $link .= "&shape=".$_GET['shape'];
      }
   }
};

Now that we have our sql statements complete the next section will build the search options.  I have used the php if statements shorthand version which follows the (condition ? if true : else) format. This is a great shortcut that can be used inline with echo and variable assignments. The link is then followed by the count for all items that match that option and meet the conditions of the WHERE statements. If the option has already been selected then a clear link becomes available in its place. This clear link will clear the set variable

$result = mysql_query($count_sql);
$cnt_qry = mysql_fetch_array($result);
if(!isset($_GET['color'])){
   echo "Color<br/>";
   echo "<a href='".((isset($link)) ? $link."&" : "?")."color=red'>Red</a>(".$cnt_qry['reds'].")<br/>";
   echo "<a href='".((isset($link)) ? $link."&" : "?")."color=blue'>Blue</a>(".$cnt_qry['blues'].")<br/>";
   echo "<a href='".((isset($link)) ? $link."&" : "?")."color=green'>Green</a>(".$cnt_qry['greens'].")<br/>";
} else {
   echo "<a href='".((isset($link)) ? $link."&" : "?")."clear=color'>Clear Color</a><br/>";
}
if(!isset($_GET['shape'])){
   echo "Shape<br/>";
   echo "<a href='".((isset($link)) ? $link."&" : "?")."shape=square'>Square</a>(".$cnt_qry['squares'].")<br/>";
   echo "<a href='".((isset($link)) ? $link."&" : "?")."shape=triangle'>Triangle</a>(".$cnt_qry['triangles'].")<br/>";
   echo "<a href='".((isset($link)) ? $link."&" : "?")."shape=circle'>Circle</a>(".$cnt_qry['circles'].")<br/>";
} else {
   echo "<a href='".((isset($link)) ? $link."&" : "?")."clear=shape'>Clear Shape</a><br/>";
}

Following the search menu output is the results of the search. This is a simple where loop on the results of the $view_sql query we built earlier. It loops through the results and outputs them to the browser.

$result = mysql_query($view_sql);
while($view_qry = mysql_fetch_array($result)){
 echo $view_qry['id'].": ".$view_qry['name']." is ".$view_qry['shape']." and ".$view_qry['color'].".<br/>";
};

This can be customized in many ways however you need with as many options as you need. You can style this menu to fit almost anywhere in your site. As long as the sql statements get run before the search bar code and the while loop those elements can be placed anywhere in the code to be run below. As always i hope this is enough to get you started and i hope i made it simple enough to understand. If you have any questions or comments please leave them below and i will answer them. If you feel you can improve on this please do by all means. If you send me your revisions i will add them here with all credit due.

Files for this demo:

Posted in Web & Database Design | Tagged , , , , , , , , , | 3 Comments

Dynamic Multi-level CSS Menu with PHP and MySQL. SEO Ready.

Since writing this post i have enhanced the script and now use V2 which you can check out here: Single Query Dynamic Multi-level Menu

I was recently working on a project for a client and couldn’t find a menu solution to fit my needs. I was looking to use a menu style similar to those found on Cssplay.co.uk, but i wanted to build the menu on the fly from a MySQL database. After many search attempts and several posts on php/mysql boards i realized i was going to have to work this out myself. I needed infinite levels and most tutorials only had options for 1 or 2 sub levels or weren’t completely dynamic and would require a table rebuild every time you add a new page.

Creating The Menu

My solution was a mix of several menus i found online. I decided to use a flat table and a simple recursive php function. The first step is working out the table structure. Here is a look at the table i used.

The MySQL Table

id label link parent sort

The id is you Primary Key field here, followed by label and link being the name and action of the option. The parent will be the id of the parent menu item and finally the sort field is used if you want any control over the order of your menu items.

Here is the sql

CREATE TABLE `menu` (
  `id` int(11) NOT NULL auto_increment,
  `label` varchar(50) NOT NULL default '',
  `link` varchar(100) NOT NULL default '#',
  `parent` int(11) NOT NULL default '0',
  `sort` int(11) default NULL,
  PRIMARY KEY  (`id`),
) ENGINE=MyISAM AUTO_INCREMENT=248 DEFAULT CHARSET=latin1;

Now that we have a table to take information from we need some data

id label link parent sort
1 Home #home 0 0
2 Code #code 0 0
3 Contact #contact 0 0
4 PHP #php 2 0
5 CSS #css 2 0
6 Scripts #scripts 4 0
7 Help #help 4 0
8 Archive #archive 6 0
9 Snippet #snippet 8 0

The links can be in any form as you will build them later. eg. ?p=84, http://url, #anchor.

The PHP Function

Now lets take a look at the php. This is a simplified version

function display_children($parent, $level) {
    $result = mysql_query("SELECT a.id, a.label, a.link, Deriv1.Count FROM `menu` a  LEFT OUTER JOIN (SELECT parent, COUNT(*) AS Count FROM `menu` GROUP BY parent) Deriv1 ON a.id = Deriv1.parent WHERE a.parent=" . $parent);
    echo "<ul>";
    while ($row = mysql_fetch_assoc($result)) {
        if ($row['Count'] > 0) {
            echo "<li><a href='" . $row['link'] . "'>" . $row['label'] . "</a>";
			display_children($row['id'], $level + 1);
			echo "</li>";
        } elseif ($row['Count']==0) {
            echo "<li><a href='" . $row['link'] . "'>" . $row['label'] . "</a></li>";
        } else;
    }
    echo "</ul>";
}

So lets start with the query.

$result = mysql_query("SELECT a.id, a.label, a.link, Deriv1.Count FROM `menu` a  LEFT OUTER JOIN (SELECT parent, COUNT(*) AS Count FROM `menu` GROUP BY parent) Deriv1 ON a.id = Deriv1.parent WHERE a.parent=" . $parent);

It looks complicated but all it is doing is getting the information for each menu item for a parent and a count of how many children it has. The results would look like this:

id label link count
1 Home #home 0
2 Code #code 2
3 Contact #contact 0

These results are the contents of the top layer or main menu, just after the sql query comes a simple echo to create our opening <ul> tags. Followed by the brains of the function inside this while statement:

while ($row = mysql_fetch_assoc($result)) {
	if ($row['Count'] > 0) {
		echo "<li><a href='" . $row['link'] . "'>" . $row['label'] . "</a>";
		display_children($row['id'], $level + 1);
		echo "</li>";
	} elseif ($row['Count']==0) {
		echo "<li><a href='" . $row['link'] . "'>" . $row['label'] . "</a></li>";
	} else;
}

This statement simply outputs the appropriate <li> tags and links for each item on the menu and then checks to see if that item has any children. If the count is more than zero then it calls the entire function over to build the child menu. Then a simple closing </ul> tag to finish it off.
To call the menu simply run the function with the level you want to display for example:

display_children(0, 1);

Would return:

display_children(4, 1);

Would return only the children of PHP or (id #4)

This second use can come in handy if you want to build a sub menu any where in your site.

The menu can be further customized as i mentioned above with css as can be seen in use at B&B Metals.

The code for the menu on that site is slightly more complex due to different css classes being used for lower levels in the menu. You can easily sort the menu by adding a sort statement to the sql statement and giving your menu items a sort order.

Styling The Menu

Now after all that it doesnt look like much yet. But im gonna show you how to make this into a horizontal dropdown menu although you could use this to make any sort of menu type.

Modifying the PHP

The first thing we have to do now is add add a class to the output of the php script.

Start by replacing:

echo "<li><a href='" . $row['link'] . "'>" . $row['label'] . "</a></li>";

With:

echo "<li class='list'><a class='list_link' href='" . $row['link'] . "'>" . $row['label'] . "</a></li>";

If you want a different style for your lower levels then you will want to use the code below instead of the one above.

echo "<li class='level".$level."'><a class='level".$level."' href='" . $row['link'] . "'>" . $row['label'] . "</a></li>";

Now that our links have been assigned a class we can style them any way we choose. If you want to style layer 2 differently then simply use the .layer2 class to change that. Now that you have your menu in the right format ie. Unordered and Ordered lists you can use just about any css menu around. I recommend checking out the ones over at Cssplay.co.uk, of course if you like there stuff make sure you donate. Another way that you can code your css menus is to give your menu a class, lets say .menu, apply that class to your top container and assign the levels of the menu as

.menu ul{color:#FFF;} /* Main container, includes the background of the static portion of the menu */
.menu ul li{color:#FFF;} /* This is the style for the main menu items */
.menu ul ul{color:#FFF;} /* This is the container for the first submenu */
.menu ul ul li{color:#FFF;} /* This is the style for the submenus */

And so on and so on. You can style the links themselves as well by using the .menu a{}. This will alter all links in the menu. To define a different style for a lower level in the menu you would simply change it to .menu ul ul a{} for the second level.

I wont go over all the styling as we would be here forever so if you have any questions feel free to ask and ill be glad to help.

Since writing this post i have enhanced the script and now use V2 which you can check out here: Single Query Dynamic Multi-level Menu

Posted in Web & Database Design | Tagged , , , , , , , , , | 31 Comments