4

Hello,

This tutorial is the next of “A simple Data Layer for OOP PHP Applications” so having read it and made your own tests is needed to understanding what we are doing. So far we have a Db object (child of PDO that standardize and simplifies its use) , a Db factory object (because our web app may need more than one DB object) and Data Workers (for working with data from DB) (there are in the tutorial mentioned above) . We are going to do it in such way that can be implemented without the use of any framework or inside the cycle of one. There are many architectural approaches in those matters and this is just one of them. We will not create here an MVC implementation (maybe this will be in a next tutorial, but there are many of those allready) , we will just create the classes and test those as if we loaded all of them from one file (I don't recommend it but we will keep it as simple as possible , and anyone can implement it in its own way).

PDO has itself a fetch style PDO::FETCH_CLASS that can fetch into an object (a real one and not anonymous stdClass). But in order for it to work , properties must have the same name with column names in the table and moreover if there is a column in the results that hasn't to do something with the object it adds it as a property to it , even if we didn't declared it (I still believe that PDO::FETCH_CLASS is a good option if no other implementation is made) . There are many different data table fields naming conventions e.g. object property parentId may be in the DB side PARENT_ID / parentId / parent_id / parent_Id or it may be something completely different (if we didn't designed it along with the app) e.g. PRNT_ID. So we will create object instantiators that automatically match the first set of naming conventions but can also accept maps to match any table field name to object property name.

PPO and the abstract Object , object.

A Plain PHP Object (PPO) is an object with private properties and public setters and getters methods for those. It doesn't mean to have methods with an extensive logic to them. They might have a bit more properties than only from the table fields or a bit more methods from getters and setters (e.g. for property active it might have isActive($active == null) that if $active there is null will return the bool property else it will set it) , as long as they don't encapsulate any business logic or view inside them. In order to abstract the object instantiator from data layer we will need a way to interact with the setters at least. There are many many ways to do it with PHP but we will go with one that seems more direct and tested for performance because will be in the core of our app. We will create an abstract Object class that all our PPO objects should inherit. Since we are here , except from the hasMethod method we will add a bit more to it for feature use (Indexed Objects List), the hasProperty and the toArray method. Type casting an object to array in PHP will return the name of the class in the key as well, in most cases what we just want as a key , is the property name, moreover here we will have the option if we want the toArray to return only the first level or deeper.

/**
 * Abstract class for objects of the model layer
 */
abstract class _Object
{

    /**
     * Returns an array with keys the properties names of the object 
     * and values their value
     * @return array
     */
    public function toArray($includeChilds = false)
    {
        // The original key => value pairs
        $array = (array)$this;
        // The result array 
        $reArray = array(); 
        foreach(array_keys($array) as $key)
        {
            // PHP merges the class name with the property name by chr(0)
            $keyParts = explode(chr(0),$key); 
            if(!is_object($array[$key]))
            {
                $reArray[$keyParts[count($keyParts)-1]]=$array[$key];
            }
            else if($includeChilds && $array[$key] instanceof _Object)
            {
                $reArray[$keyParts[count($keyParts)-1]] 
                    = $array[$key]->toArray(true);
            }
        }
        return $reArray;
    }


    /**
     * Return if the given property name exists in the object
     * @param string $propertyName
     * @return boolean
     */
    public function propertyExist($propertyName)
    {
        return property_exists($this, $propertyName);
    }

    /**
     * Return if the object has the given method name.
     * @param string $methodName
     * @return boolean
     */
    public function methodExists($methodName)
    {
        return method_exists($this, $methodName);
    }

    /**
     * Returns the class name of the object
     * @return string
     */
    public static function className()
    {
        return get_called_class();
    }
}

Object Instantiators Parent Class

Object Instantiators are still data workers , all they have more (for now) is a method to map an array to an object. As we mentioned in the introduction this must be through common naming conventions (e.g. parentId / PARENT_ID / parent_Id) or pass an array to map the table field name to property name.

/**
 * Parent class for Object Instantiators 
 */
abstract class _Object_Instantiator extends _Data_Worker
{

    /**
     * Assigns values to properties of an object from an associative array.
     * @param array $array . The array to be mapped. If $mapper is not set then native behavior will
     * be used(e.g. array key value $r["categoryId"] will mapped to object's property $categoryId) 
     * if the object has a method setCategoryId($categoryId).
     * @param _Object $object The object that values will map to.
     * @param string $mapper defult empty array. If passed will map the table field to the proper property 
     * e.g. "CAT_ID" => "id" will set the id property of the object from the CAT_ID field value
     */
    public function map($array,_Object $object,$mapper = array())
    {
            foreach( $array as $key => $value )
            {
                if( isset( $mapper[$key]) )
                {       
                    $methodName = "set".ucfirst( $mapper[$key] );
                }
                else if(strpos($key,"_") !== false)
                {
                    $methodName = "set";
                    $keyParts = explode("_", $key);
                    foreach($keyParts as $keyPart)
                    {
                        $methodName .= ucfirst(strtolower($keyPart));
                    }
                }
                else 
                {
                    $methodName = "set".ucfirst($key);
                }

                if($object->methodExists($methodName))
                {
                    $object->$methodName($value);
                }
            }
    }
}

Example of Usage #1

First we will test it with the “normal” naming conventions (that is not the case usally) we will have a categories table with id , parentId , url (the url segment of the category) , priority (the priority inside the same level) and status (0 inactive , 1 active). Hear are some data to test

--
-- Table structure for table `categories`
--

CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `parentId` int(10) NOT NULL,
  `title` varchar(255) COLLATE utf8_bin NOT NULL,
  `url` varchar(255) COLLATE utf8_bin NOT NULL,
  `priority` int(5) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL COMMENT '0 Inactive / 1 Active',
  PRIMARY KEY (`id`),
  UNIQUE KEY `parentIdurlInd` (`parentId`,`url`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=25 ;

--
-- Dumping data for table `categories`
--

INSERT INTO `categories` (`id`, `parentId`, `title`, `url`, `priority`, `status`) VALUES
(1, 0, 'News', 'news', 10, 1),
(2, 1, 'Internet', 'internet', 30, 1),
(3, 1, 'Newspapers', 'newspapers', 20, 1),
(4, 1, 'Television', 'television', 40, 1),
(5, 1, 'Current Events', 'current-events', 10, 1),
(6, 0, 'Computers', 'computers', 20, 1),
(8, 6, 'Hardware', 'hardware', 30, 1),
(9, 6, 'Networking', 'networking', 10, 1),
(10, 6, 'Security & Encryption', 'security-encryption', 30, 1),
(11, 6, 'Robotics', 'robotics', 20, 1),
(12, 6, 'Internet', 'internet', 50, 1),
(13, 6, 'Mobile Computing', 'mobile-computing', 60, 1);

The Category PPO object:

class Model_Object_Category extends _Object
{
    private $id;
    private $parentId;
    private $title;
    private $url;
    private $priority;
    private $status;

    public function setId($id)
    {
        $this->id = $id;
    }

    public function setParentid($parentId)
    {
        $this->parentId = $parentId;
    }

    public function setTitle($title)
    {
        $this->title = $title;
    }

    public function setUrl($url)
    {
        $this->url = $url;
    }

    public function setPriority($priority)
    {
        $this->priority = $priority;
    }

    public function setStatus($status)
    {
        $this->status = $status;
    }


    /** int(10) NOT NULL AUTO_INCREMENT */
    public function getId()
    {
        return $this->id;
    }

    /** int(10) NOT NULL */
    public function getParentid()
    {
        return $this->parentid;
    }

    /** varchar(255) COLLATE utf8_bin NOT NULL */
    public function getTitle()
    {
        return $this->title;
    }

    /** varchar(255) COLLATE utf8_bin NOT NULL */
    public function getUrl()
    {
        return $this->url;
    }

    /** int(5) NOT NULL DEFAULT '0' */
    public function getPriority()
    {
        return $this->priority;
    }

    /** tinyint(1) NOT NULL COMMENT '0 Inactive / 1 Active' */
    public function getStatus()
    {
        return $this->status;
    }
}

The Category Data Worker:

class Model_Data_Category extends _Data_Worker
{
    private $insertSql = "INSERT INTO categories (parentId,title,url,priority,status) VALUES (?,?,?,?,?)";
    private $selectAllSql = "SELECT * FROM categories ORDER BY priority ASC";
    private $selectSql = "SELECT * FROM categories WHERE id = ?";
    private $deleteSql = "DELETE FROM categories WHERE id = ?";
    private $updateParentidSql = "UPDATE categories SET parentId = ? WHERE id = ?";
    private $updateTitleSql = "UPDATE categories SET title = ? WHERE id = ?";
    private $updateUrlSql = "UPDATE categories SET url = ? WHERE id = ?";
    private $updatePrioritySql = "UPDATE categories SET priority = ? WHERE id = ?";
    private $updateStatusSql = "UPDATE categories SET status = ? WHERE id = ?";

    public function insert($parentid,$title,$url,$priority,$status)
    {
        $this->db()->request($this->insertSql,array($parentid,$title,$url,$priority,$status));
    }

    public function selectAll()
    {
        return $this->db()->request($this->selectAllSql);
    }

    public function select($id)
    {
        return $this->db()->request($this->selectSql,$id);
    }

    public function delete($id)
    {
        $this->db()->request($this->deleteSql,$id);
    }

    public function updateParentid($parentid,$id)
    {
        $this->db()->request($this->updateParentidSql,array($parentid,$id));
    }

    public function updateTitle($title,$id)
    {
        $this->db()->request($this->updateTitleSql,array($title,$id));
    }

    public function updateUrl($url,$id)
    {
        $this->db()->request($this->updateUrlSql,array($url,$id));
    }

    public function updatePriority($priority,$id)
    {
        $this->db()->request($this->updatePrioritySql,array($priority,$id));
    }

    public function updateStatus($status,$id)
    {
        $this->db()->request($this->updateStatusSql,array($status,$id));
    }

}

The Category Object Instantiator:

(for now we will have only one method to get the object by id)

class Model_Instantiator_Category extends _Object_Instantiator
{

    /**
     * Returns a category object using data from db id this id 
     * exists or a category object with null properties values 
     * if doesn't 
     * @param int $id
     * @param array $mapper default empty array
     * @return Model_Object_Category
     */
    public function getById($id,$mapper = array())
    {
        $o = new Model_Object_Category();
        $data = new Model_Data_Category($this->dbFactory(),$this->dbMaps());
        $r = $data->select($id);
        if(count($r) > 0)
        {
            $this->map($r[0], $o,$mapper);
        }
        return $o;
    }
}

Use of it:

// This array could be decoded by a json file above public_html
$dbPropertiesArray =
array(
        ""=>
            array("database" => "test",
            "username" => "testuser",
            "password" => "testpassword",
            "charset" => "UTF8")
);

$dbFactory = new _Data_DbFactory($dbPropertiesArray);

$inst = new Model_Instantiator_Category($dbFactory);
/* @var $category Model_Object_Category */
$category = $inst->getById(3);
if($category->getId() != null)
{
    // There is a category with id 3 DO SOMETHING   
}

Example of Usage #2

Sadly most tables don't have fields with such names , let's suppose that the fields of this table are:

ALTER TABLE  `categories` CHANGE  `id`  `CAT_ID` INT( 10 ) NOT NULL AUTO_INCREMENT ;
ALTER TABLE  `categories` CHANGE  `parentId`  `PRNT_CAT_ID` INT( 10 ) NOT NULL ;
ALTER TABLE  `categories` CHANGE  `title`  `TITLE` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ;
ALTER TABLE  `categories` CHANGE  `url`  `URL_SEGMNT` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ;
ALTER TABLE  `categories` CHANGE  `priority`  `PRIORITY` INT( 5 ) NOT NULL DEFAULT  '0';
ALTER TABLE  `categories` CHANGE  `status`  `STATUS` TINYINT( 1 ) NOT NULL COMMENT  '0 Inactive / 1 Active';

Now all we have to do is change a bit the data worker (only the query strings not the methods ) and pass a mapper array containing only those fields that don't follow common naming conventions

class Model_Data_Category extends _Data_Worker
{
    private $insertSql = "INSERT INTO categories (PRNT_CAT_ID,TITLE,URL_SEGMNT,PRIORITY,STATUS) VALUES (?,?,?,?,?)";
    private $selectAllSql = "SELECT * FROM categories ORDER BY PRIORITY ASC";
    private $selectSql = "SELECT * FROM categories WHERE CAT_ID = ?";
    private $deleteSql = "DELETE FROM categories WHERE CAT_ID = ?";
    private $updateParentidSql = "UPDATE categories SET PRNT_CAT_ID = ? WHERE CAT_ID = ?";
    private $updateTitleSql = "UPDATE categories SET TITLE = ? WHERE CAT_ID = ?";
    private $updateUrlSql = "UPDATE categories SET URL_SEGMNT = ? WHERE CAT_ID = ?";
    private $updatePrioritySql = "UPDATE categories SET PRIORITY = ? WHERE CAT_ID = ?";
    private $updateStatusSql = "UPDATE categories SET STATUS = ? WHERE CAT_ID = ?";

// ... methods are the same
}

And finally:

// This array could be decoded by a json file above public_html
$dbPropertiesArray =
array(
        ""=>
            array("database" => "test",
            "username" => "testuser",
            "password" => "testpassword",
            "charset" => "UTF8")
);

$dbFactory = new _Data_DbFactory($dbPropertiesArray);

$inst = new Model_Instantiator_Category($dbFactory);
/* @var $category Model_Object_Category */
$category = $inst->getById(3
    ,array( "CAT_ID"=>"id" , "PRNT_CAT_ID" => "parentId" , "URL_SEGMNT" => "url"));
if($category->getId() != null)
{
    // There is a category with id 3 DO SOMETHING   
}

Comments are more than welcomed , also if anyone who follow these tutorials chain has any questions or opinions if it has meaning to move to Indexed List Model and Application Scope Caching.

Votes + Comments
That's really neat jkon. I particularly like the data worker. I'll give it a whirl when I get some time. Thanks
1
Contributor
1
Reply
43
Views
2 Years
Discussion Span
Last Post by jkon
0

Hello, thank you for the votes and for the notes on these tutorials (any question might arose by testing I would be happy to give more details) . The need to write those came from one thread where diafol and I exchanged ideas of how to solve a problem , there I mentioned the Indexed List Model and Application Scope Caching but I couldn't give more details because I had to write about Db object , Db factory , Data Workers and Object Instantiators first , so I just gave a crude example using arrays instead.

One thing I hear a lot about OOP in general , but also about that model approach is that it is over verbose. I know that some might have the question “Should I write PPO , Data Workers and Instantiators by hand , even when there is tight coupling ?” (This approach doesn't rely in tight coupling between table fields and object properties , the properties could arose from any query result and the methods of data workers could use one or many private sql query strings in order to achieve what is needed (for example if you delete a category and that some other field in another table is depending on that you could do it in the same method) (keep in mind that this is a minified version making it as simple as possible , but no simpler than that) ).

The short answer to that , would easily be “code generators” , but having worked in environments where these automatic code generators created a mess , have strengthen my opinion that no program should auto generate code inside another one. One easy solution in order not to write by hand those classes when there is tight coupling and a primary unique key is to create a code generator and let the programmer decide how this code will be used (this is the skeleton) . That is what this Javascript class + html does. It takes the result from “SHOW CREATE TABLE x” , a class name for the PPO object (also mappers if any) and produces the PPO object class the Data Worker and the Object Instantiator. I thought to add a link but I believe that is better to be here along with this tutorial.

<!DOCTYPE html>
<html>
<head>
<title>DB Table to PPO Object Generator (Data Worker &amp; Instantiator)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="robots" content="noindex, nofollow">
</head>
<body>
    Object Class:
    <input type="text" size="50" maxlength="500" id="className" />
    <br /> SHOW CREATE TABLE x :
    <br />
    <textarea rows="5" cols="40" id="createTable"></textarea>
    <br />
    <br />
    <small>Db Table in Object mappers (if any) in JSON format without { and } e.g.:
        "CAT_PRNT_ID":"parentId","CAT_ID":"id"</small>
    <br />
    <input type="text" size="50" maxlength="500" id="mappers" />
    <br />
    <small>While copying the code generated , take a moment to look at it and understand what it does. That  way you can adjust it in your needs more easy. </small>
    <br />
    <a href="javascript:proceed();">PROCEED</a>
    <br />
    <br />
    <b>PPO Object:</b>
    <br />
    <textarea rows="10" cols="80" id="objectClass"></textarea>
    <br />
    <br />
    <b>Data Worker:</b>
    <br />
    <textarea rows="10" cols="80" id="dataWorkerClass"></textarea>
    <br />
    <br />
    <b>Instantiator:</b>
    <br />
    <textarea rows="10" cols="80" id="instantiatorClass"></textarea>
    <script>

    function proceed()
    {
        var parser = new TableParser( getValue("className")
                , getValue("createTable") , getValue("mappers") );
        setValue( "objectClass",parser.getPpoClass() );
        setValue( "dataWorkerClass",parser.getDataWorkerClass() );
        setValue( "instantiatorClass",parser.getInstantiatorClass() );
    }

    function TableParser(className,createTable,mappersString)
    {
        var _this = this; 
        var fieldsPropertiesMap = {};
        var fieldsComments = {};
        var mappers = {};
        var tableName = "";
        var primaryKey = "";
        var primaryKeyCamel = "";

        /**
         *   Inits the object 
         */
        function init()
        {
            if(mappersString !== "")
            {
                mappers = JSON.parse("{"+mappersString+"}");
            }
            parseCreateTable();
        }

        /**
         *   Creates and returns the PPO object class 
         */     
        this.getPpoClass = function()
        {
            var re = "";

            re += "<\?php\n";
            re += "class "+className+" extends _Object\n";
            re += "{\n";
            // The properties 
            for(key in fieldsPropertiesMap)
            {
                re += "\tprivate $"+fieldsPropertiesMap[key]+";\n";
            }
            re += "\n";
            re += "\n";
            // Setters 
            for(key in fieldsPropertiesMap)
            {
                re += "\n";
                re += "\tpublic function set"
                    +fieldsPropertiesMap[key].charAt(0).toUpperCase() 
                    + fieldsPropertiesMap[key].substr(1)
                    +"( $"+fieldsPropertiesMap[key]+" )\n";
                re += "\t{\n";
                re += "\t\t$this->"+fieldsPropertiesMap[key]+" = $"
                    +fieldsPropertiesMap[key]+";\n";
                re += "\t}\n";

            }           
            re += "\n";

            // Getters 
            for(key in fieldsPropertiesMap)
            {
                re += "\n";
                re += "\t/**\n";
                re += "\t * @return "+fieldsComments[key]+"\n";
                re += "\t */\n";
                re += "\tpublic function get"
                    +fieldsPropertiesMap[key].charAt(0).toUpperCase() 
                    + fieldsPropertiesMap[key].substr(1)
                    +"()\n";
                re += "\t{\n";
                re += "\t\treturn $this->"+fieldsPropertiesMap[key]+";\n";
                re += "\t}\n";
            }                       
            re += "}\n";
            re += "?>";
            return re; 
        };

        /**
         *   Creates and returns the Data Worker class 
         */ 
        this.getDataWorkerClass = function()
        {
            var re = "";

            re += "<\?php\n";
            re += "class "+className.replace("Object_","Data_")
                +" extends _Data_Worker\n";
            re += "{\n";

            var properties = "";
            var methods = ""; 

            var insertFields = []; 
            var insertProperties = []; 
            var insertPlaceholders = []; 

            var isFirst = true; 
            for(key in fieldsPropertiesMap)
            {
                if(isFirst)
                {
                    isFirst = false; 
                }
                else 
                {
                    insertFields.push(key);
                    insertProperties.push("$"+fieldsPropertiesMap[key]);
                    insertPlaceholders.push("?");
                }
            }

            // Insert 
            properties += "\tprivate $insertSql = \"INSERT INTO " + tableName 
                + " (" + insertFields.join(",") + ") VALUES (" 
                + insertPlaceholders.join(",") + ")\";\n";

            methods += "\tpublic function insert(" + insertProperties.join(",") 
                + ")\n";
            methods += "\t{\n";
            methods += "\t\t$this->db()->request( $this->insertSql,array("
                +insertProperties.join(",")+") );\n";
            methods += "\t\treturn $this->db()->lastInsertId();\n";
            methods += "\t}\n";
            methods += "\n";

            // Select All 
            properties += "\tprivate $selectAllSql = \"SELECT * FROM " + tableName 
                + " ORDER BY " + primaryKey + " ASC\";\n";  
            methods += "\tpublic function selectAll()\n";
            methods += "\t{\n";
            methods += "\t\treturn $this->db()->request( $this->selectAllSql );\n";
            methods += "\t}\n";
            methods += "\n";            

            // Select by primary key 
            properties += "\tprivate $selectBy" + primaryKeyCamel
                + "Sql = \"SELECT * FROM " + tableName 
                + " WHERE "+primaryKey+" = ?\";\n";
            methods += "\tpublic function selectBy" + primaryKeyCamel
                + "($"+primaryKey.toLowerCase()+")\n";
            methods += "\t{\n";
            methods += "\t\treturn $this->db()->request( $this->selectBy" 
                + primaryKeyCamel + "Sql,$" + primaryKey.toLowerCase() + " );\n";

            methods += "\t}\n";
            methods += "\n";    

            // UPDATE 
            for(key in fieldsPropertiesMap)
            {
                if(key !== primaryKey)
                {
                    var camelCase = fieldsPropertiesMap[key].charAt(0).toUpperCase() 
                        + fieldsPropertiesMap[key].substr(1).toLowerCase();
                    properties += "\tprivate $update" + camelCase 
                        + "Sql = \"UPDATE " + tableName 
                        + " SET " + key + " = ? WHERE "+primaryKey+" = ?\";\n";

                    methods += "\tpublic function update" + camelCase
                        + "( $"+fieldsPropertiesMap[key]+" , $" + fieldsPropertiesMap[primaryKey] +" )\n";
                    methods += "\t{\n";
                    methods += "\t\t$this->db()->request( $this->update" 
                        + camelCase + "Sql , array( $" +fieldsPropertiesMap[key]+ " , $"
                        + fieldsPropertiesMap[primaryKey] +" ) );\n";
                    methods += "\t}\n";
                    methods += "\n";                        
                }
            }

            var isPrimary = true; 


            re += properties; 
            re += "\n";
            re += methods; 

            re += "}\n";
            re += "?>";
            return re; 
        }

        this.getInstantiatorClass = function()
        {
            var re = "";

            re += "<\?php\n";
            re += "class "+className.replace("Object_","Instantiator_")
                +" extends _Object_Instantiator\n";
            re += "{\n";


            re += "\n";
            re += "\t/**\n";
            re += "\t * @return "+className+"\n";
            re += "\t */\n";
            re += "\tpublic function getBy"+primaryKeyCamel
                +"($"+fieldsPropertiesMap[primaryKey]+",$mapper = array())\n"
            re += "\t{\n";
            re += "\t\t$o = new "+className+"();\n";
            re += "\t\t$data = new "+className.replace("Object_","Data_")+"($this->dbFactory(),$this->dbMaps());\n";
            re += "\t\t$r = $data->selectBy"+primaryKeyCamel+"($"
                +fieldsPropertiesMap[primaryKeyCamel]+");\n";
            re += "\t\tif(count($r) > 0)\n"
                re += "\t\t{\n";
                re += "\t\t\t$this->map($r[0],$o,$mapper);\n";
                re += "\t\t}\n";
                re += "\t\treturn $o;\n";
            re += "\t}\n";

            re += "\n";
            re += "\t/**\n";
            re += "\t * @return _List\n";
            re += "\t */\n";
            re += "\tpublic function getList"
                +"($mapper = array())\n"
            re += "\t{\n";
            re += "\t\t$list = new _List("+className+"::className());\n";
            re += "\t\t$list->setIndexForObjects(\""+fieldsPropertiesMap[primaryKey]+"\");\n";
            re += "\t\t$data = new "+className.replace("Object_","Data_")+"($this->dbFactory(),$this->dbMaps());\n";
            re += "\t\t$r = $data->selectAll();\n";
            re += "\t\t$this->mapList($r,$list,$mapper);\n";
            re += "\t\treturn $list;\n";
            re += "\t}\n";

            re += "}\n";
            re += "?>";
            return re; 
        };

        /**
         *  Private method that parses the "SHOW CREATE TABLE x" result
         *  into data table field names , object properties name and 
         *  comments about the stracture of the data in DB side  
         */
        function parseCreateTable()
        {
            var lines = createTable.split(/\n/);    

            // finding the table name
            tableName = lines[0].substr(lines[0].indexOf("`") + 1
                    , lines[0].length);
            tableName = tableName.substr(0,tableName.indexOf("`"));

            for(var i in lines)
            {
                var line = lines[i];
                // if line start with grave accent then it is contains a field
                if (line.indexOf("`") == 1 )
                {
                    // remove the first grave accent
                    line = line.substr(2); 
                    // now the field name is before the first grave accent
                    var accentIndex = line.indexOf("`");
                    var field = line.substr(0,accentIndex);
                    // fieldsComments here is the data type of the field plus any comments
                    fieldsComments[field] = line.substr(accentIndex+1
                            ,line.length - accentIndex - 2); 
                    // if there is a mapper for that field 
                    if(mappers[field] != undefined)
                    {
                        fieldsPropertiesMap[field] = mappers[field];
                    }
                    else 
                    {
                        var property = "";
                        // the _ rule we will get each part after the first with 
                        // a capitalized first letter 
                        if(field.indexOf("_" !== -1))
                        {
                            var parts = field.split("_");
                            property += parts[0].toLowerCase();
                            for(var i2 = 1; i2 < parts.length; i2++)
                            {
                                var part = parts[i2].toLowerCase();
                                property += part.charAt(0).toUpperCase() + part.substr(1);
                            }
                        }
                        else 
                        {
                            property = field.charAt(0).toLowerCase() + part.substr(1);
                        }
                        // adding it to the fieldsPropertiesMap object
                        fieldsPropertiesMap[field] = property;
                        if(primaryKey == "")
                        {
                            primaryKey = field;
                            primaryKeyCamel = property.charAt(0).toUpperCase() 
                                + property.substr(1);
                        }
                    }
                }
            }
        }

        init();
    }


    function getValue(id)
    {
        return document.getElementById(id).value.trim();
    }

    function setValue(id,value)
    {
        document.getElementById(id).value = value.trim();
    }
</script>
</body>
</html>
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.