Wednesday, July 28, 2010

HowTo navigate through a tree without recursive methods (while loop instead)

-Hi,
today I was simply writing a local template in order to render a menu following the MVC pattern.
Now my local template system does not use a meta-language, but loops, if statements and so on are written directly in php, because I don't have enough time to write an interpreter.
So really I can use all php functionalities in my templates including recursive functions. But it's not the way I want to follow, moreover maybe some day I'll find the time to write my interpreter and then will be very easy to "convert" if statements and cycles, not so easy if not impossible to convert recursive functions.
Well, so the problem was the following:
I have a n-dimensional menu tree, and have to generate the classical html code for such a menu, that is something like:
<ul id="nav">
    <li>Voice1</li>
    <li>Voice2
        <ul>
            <li>Voice21</li>
            <li>Voice22</li>
        </ul>
    </li>
    <li>Voice3</li>
</ul>
So let's see HOW TO get this without recursion. The code is well commented, here it goes:
<?php

/*
 * Menu tree is represented by an n-dimensional array.
 * Array keys are the labels and array values are either a link
 * or a submenu (another array)
 */
$voices["Home"] = array("Home1" => array("Home11"=>"#"), "Home2"=>array("Home21"=>"#"));
$voices["News"] = array("News1" => "#", "News2"=>array("News21"=>"#"));
$voices[_("About")] = array("About1"=>array("About11"=>array("About111"=>"#", "About112"=>"#")), "About2"=>"#");
$voices[_("Services")] = '#';
$voices[_("Faq")] = '#';

/*
 *  INIT SOME VARIABLES
 */
// control the exit from the loop
$continue = true;
// this variables stores the branch currently analized
$parsed = $voices;         
// stores all the branches parsered, because the tree is navigated following a branch
// until the foil. So when returning on top levels the navigation must continue on
// branches interrupted earlier.
$tree = array();
// stores the last key of the current branch array
$last = null;

echo "<ul id=\"nav\">\n";

/*
 *  THE MAIN LOOP
 */
while($continue === true) {
    // if the value of the current element parsered is an array than has a submenu
    if(is_array(current($parsed))) {
        // echo the current voice and open a new submenu (ul)
        echo "<li><a href=\"#\">".key($parsed)."</a>\n<ul>\n";
        // we're going to begin navigate the branch ->
        // we store the branch we're leaving in the tree array
        $tree[] = $parsed;
        // the branch to analyze is now the submenu
        $parsed = current($parsed);
        // get the last key of the new branch
        end($parsed);
        $last = key($parsed);
        // reset the array pointer
        reset($parsed);
    }
    // else if the value of the current element is not an array (but a link)
    // and not false (i.e. after calling next on the last element of an array)
    elseif(current($parsed)!==false) {
        // echo the current voice
        echo "<li><a href=\"".current($parsed)."\">".key($parsed)."</a></li>\n";

        // if the voice printed is the last of its branch
        // than close the submenu and get the last branch stored in the
        // varable tree (the parent branch) as the one to follow parsering
        if(key($parsed)==$last) {
            echo "</ul></li>\n";
            $parsed = array_pop($tree);
        }
        // move the pointer to the next element
        next($parsed);
    }  
    // else the value of the current element is false -> we have already passed the last element
    // then we close the submenu and get the parent branch as the current one, passing to the next element
    else {
        echo "</ul></li>";
        $parsed = array_pop($tree);
        next($parsed);
    }

    // if we are navigating the first tree level and have passed through all them -> exit
    if(count($tree)==0 && current($parsed)==false) $continue = false;
}

echo "</ul>\n";
?>
Hasta la proxima!

Friday, July 9, 2010

Get MySQL table data structure with PHP

Hi, today I'll show how to retrieve informations about a MySQL table through php in order for example to create a php class making editable a table in an automatic way (that is: stupid class, i give you a name of a table and YOU have to create its backend for me). This work is done very well by the python framework Django for example.
Now, there are several ways to do so, I'll use the interrogation of the information_schema db.
Well, let's see the code. I post here a simple function, clearly the informations that may be retrieved are more than these.
function getTableStructure($dbname, $table) {
    $structure = array();
    $fields = array();

    $query = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '$dbname' AND TABLE_NAME = '$table'";
    $res = mysql_query($query);

    while($row = mysql_fetch_array($res)) {
        $fields[$row['COLUMN_NAME']] = array(
            "order"=>$row['ORDINAL_POSITION'],
            "default"=>$row['COLUMN_DEFAULT'],
            "null"=>$row['IS_NULLABLE'],
            "type"=>$row['DATA_TYPE'],
            "max_length"=>$row['CHARACTER_MAXIMUM_LENGTH'],
            "key"=>$row['COLUMN_KEY'],
            "extra"=>$row['EXTRA']
        );
        if($row['COLUMN_KEY']=='PRI') $structure['primary_key'] = $row['COLUMN_NAME'];
    }
    $structure['fields'] = $fields;

    return $structure;

}

The important informations to do an auto-generation-form class are:
  • default: the default value (we may insert it as a default value in the input field of the form)
  • null: we may use it to decide whether a field must be compulsory or not
  • type: the must important: which form element we'll use? It depends on data type and ...
  • max_length: the maximum number of characters acceopted for the field
  • key: we may want to check for uniques keys etc...
  • extra: i think it's useful to know if a field is auto_increment because we may not make it editable
That's all falks, hasta la proxima!