<?php
/**
* Defines a persistent object using PDO and Databases
* It supports, basically, PostgreSQL, MySQL, MSSQL and
* Oracle databases.
* Things get easy when all you need is:
* $obj = new Person();
* $obj->name = "John Doe";
* $obj->phone = 55555555;
* $obj->save();
*
* @author Pablo Santiago Sánchez <hide@address.com>
* @copyright Copyright (c) 2008, Pablo Santiago Sánchez
* @license http://opensource.org/licenses/bsd-license.php BSD License
* @package pop
* @subpackage core
*/
/**
* Class used as base for all persistent objects created under
* the POP paradigm
*
* @package pop
* @subpackage core
*/
abstract class Persist
{
/**
* Used to hold the id of the object on the database
*
* @access protected
* @var string
*/
protected $id;
/**
* Used to load objects specialized from existent parents
*
* @access protected
* @var string
*/
protected $id_self;
/**
* Database connection that will be used by the object/class must be a
* PDO connection object protected $pop_db;
*
* @access protected
* @var string
*/
protected $pop_db = "pop_db";
/**
* Type of the database in use
*
* @access protected
* @var string
*/
protected $pop_dbtype;
/**
* Internal use - An Instance of the class from which this extends
*
* @access protected
* @var string
*/
protected $pop_parent;
/**
* Internal use - The class name from which this extends
*
* @access protected
* @var string
*/
protected $pop_parent_type;
/**
* Internal use - Array of Attributes of this object
*
* @access protected
* @var string
*/
protected $pop_attrs;
/**
* Internal use - Array of Attributes of this object parents
*
* @access protected
* @var string
*/
protected $pop_parent_attrs;
/**
* Internal use - Array of Attributes only avaliable on this object
*
* @access protected
* @var string
*/
protected $pop_self_attrs;
/**
* Internal use - Name of the schema on the database
*
* @access protected
* @var string
*/
protected $pop_schema;
/**
* Name of the schema on the database declared for metamapping
*
* @access protected
* @var string
*/
protected $schema;
/**
* Internal use - Name of the table on the database
*
* @access protected
* @var string
*/
protected $pop_table;
/**
* Name of the table on the database declared for metamapping
*
* @access protected
* @var string
*/
protected $table;
/**
* Name of the parent's schema on the database declared for metamapping
*
* @access protected
* @var string
*/
protected $pop_parent_schema;
/**
* Name of the parent's table on the database declared for metamapping
*
* @access protected
* @var string
*/
protected $pop_parent_table;
/**
* Holds the driver for the current database
*
* @access protected
* @var string
*/
protected $pop_db_driver;
/**
* Classes that contains this class (used for ORM)
*
* @access protected
* @var string
*/
protected $parent_classes;
/**
* Metamapping of Attributes and Fields on the Database
* @access protected
* @var string
*/
protected $meta_mapping;
/**
* this is the only thing that will be changed on classes that extends
* Persist __construct on extended classes must call the
* parent::__construct() method due to the fact that the data typing of
* attributes must be set in the constructor
*/
public function __construct()
{
//creates the ID attribute, the only one that cannot be set manually
$this->id = new PInteger();
//object must be initialized
$this->initialize();
}
/**
* Magic method used to set the value and check if it's valid
*
* @param string $name name of the value
* @param string $value value itself
* @return bool
*/
public function __set($name, $value)
{
try
{
if (get_class($this->$name) != "PArrayOf")
{
$this->$name->value = trim($value);
if ($this->pop_parent && isset($this->pop_parent->$name))
$this->pop_parent->set($name,trim($value));
return true;
}
else
{
throw new Exception("You cannot set the ID attribute manually. It must be given by the database.");
}
}
catch (Exception $e)
{
throw new Exception("Error setting ".$name.": ".$e->getMessage());
}
}
/**
* Magic method used to get the value of an attribute
*
* @param string $name name of the value
* @return mixed
*/
public function __get($name)
{
if (isset($this->$name) && get_class($this->$name)=="PArrayOf")
return $this->$name->array;
else if (isset($this->$name))
return $this->$name->value;
}
/**
* Method used to set the value and check if it's valid
*
* @param string $name name of the value
* @param string $value value itself
* @return bool
*/
public function set($name, $value)
{
$this->__set($name, $value);
}
/**
* Method used to get the value of an attribute
*
* @param string $name name of the value
* @return mixed
*/
public function get($name)
{
return $this->__get($name);
}
/**
* Method used to load the object with the id value passed, or
* can receive an array of attributes and values it should return
*
* @param mixed $value Integer of the ID on database or other criterias as an array
* @return mixed
*/
public function load($value)
{
if ($value)
{
$sql = "select";
$tables = array();
$tables[$this->getTargetName()] = $this->getTargetName();
$sql .= "\n\t".$this->getTargetName().".".$this->getMetaName("id")." as id";
$comma = ",";
//first, we get our own metas
foreach ($this->pop_self_attrs as $key=>$attr)
{
if (get_class($this->$key) != "PArrayOf" && $this->checkAttrName($key))
{
$type = get_class($this->$key);
$sql .= $comma."\n\t".$this->pop_db_driver->filterSelect($this->getTargetName(), $this->getMetaName($key), $key, $type);
}
}
//now, we get our parents metas
$parents = $this->getParentMetaAttrs();
foreach ($parents as $table=>$attrs)
{
$tables[$table] = $table;
foreach ($attrs as $attr=>$meta)
if (get_class($this->$attr) != "PArrayOf" && $this->checkAttrName($attr))
{
$type = get_class($this->$attr);
$sql .= $comma."\n\t".$this->pop_db_driver->filterSelect($table, $meta, $attr, $type);
}
}
//now we load the id of each object and it's parents
foreach ($tables as $table)
{
if (strstr($table, '.'))
$tablename = explode(".",$table);
else
$tablename = array("",$table);
$sql .= $comma."\n\t".$table.".".$this->getMetaName("id")." as id_self_".$tablename[1];
}
//now, we "from" with left joins
$sql .= "\nfrom";
$sql_left_joins = "";
$count = count($tables) - 1;
$i = 0;
foreach ($tables as $table)
{
if ($i==$count)
$sql_left_joins = "\n\t".$table.$sql_left_joins;
if ($i!=0)
$sql_left_joins = str_replace("{next_table_id}",$table.".".$this->getMetaName("id"), $sql_left_joins);
if ($i!=$count)
$sql_left_joins = "\n\t\tleft join ".$table." on ".$table.".".$this->getMetaName("id")." = {next_table_id}".$sql_left_joins;
$i++;
}
$sql .= $sql_left_joins;
//now, we "where"
$sql .= "\nwhere";
$and = "";
//now, we'll get by the selected attributes.
//now, we build the where clause
if (is_array($value))
{
foreach ($value as $key => $paramvalue)
{
$in_parent = false;
$in_this = false;
//again, we must check what is not in the parent
if ($this->checkParentAttrExistence($key))
$in_parent = true;
if ($this->checkAttrExistence($key))
$in_this = true;
if (!$in_parent && $in_this)
{
if (is_int($paramvalue) || is_float($paramvalue))
$sql .= $and."\n\t".$this->getTargetName().".".$this->getMetaName($key)." = ".$paramvalue;
else
$sql .= $and."\n\t".$this->getTargetName().".".$this->getMetaName($key)." = '".$paramvalue."'";
}
else
{
foreach ($parents as $table=>$attrs)
{
if (isset($attrs->$key))
{
if (is_int($paramvalue) || is_float($paramvalue))
$sql .= $and."\n\t".$table.".".$this->getMetaName($key)." = ".$paramvalue;
else
$sql .= $and."\n\t".$table.".".$this->getMetaName($key)." = '".$paramvalue."'";
}
}
if ($key=="id")
$sql .= $and."\n\t".$table.".".$this->getMetaName($key)." = ".$paramvalue;
}
$and = " and ";
}
}
else
$sql .= $and."\n\t".$table.".".$this->getMetaName("id")." = ".$value;
/****************still must check code************/
try
{
if (POPEnvironment::$debug)
echo "<pre><font color=#009900>".$sql."</font></pre><br>";
$rs = POPDB::getConnection($this->pop_db)->query($sql);
$result = $rs->fetchAll();
$rs->closeCursor();
if (!$result)
{
throw new Exception("No data found by this criteria.\n");
}
$count = 0;
foreach ($result as $row)
{
$count++;
foreach ($this->pop_attrs as $key => $objvalue)
{
if ($this->checkAttrName($key) && get_class($objvalue)!= "PArrayOf" && (isset($row[$key]) || isset($row[strtoupper($key)])))
$this->set($key,$row[$this->pop_db_driver->filterResult($key)]);
else if (get_class($objvalue)== "PArrayOf")
$this->$key->reset();
}
//acrescentar o carregamento do id_self aqui
$temp_selves = array();
foreach ($row as $key => $cell)
{
if (strstr($key,"id_self") || strstr($key,"ID_SELF"))
$temp_selves[$key] = $row[$this->pop_db_driver->filterResult($key)];
}
$this->setIdSelf($temp_selves);
}
if ($count == 1)
return true;
else
{
throw new Exception("More than one result returned. Load criteria must be more specific.\n");
}
}
catch (Exception $e)
{
throw new Exception('Invalid SQL: <font color=#FF0000>"'.$sql.'"</font>'."\n". $e->getMessage());
}
}
}
/**
* Method used to set the value of $id_self, used for extended classes
*
* @param array $arr
*/
protected function setIdSelf($arr)
{
$idselfname = "id_self_".$this->pop_table;
if (!isset($arr[$idselfname]))
$idselfname = substr($idselfname, 0, 30);
$this->id_self = $arr[$idselfname];
$this->id->value = $this->id_self;
if ($this->pop_parent_type != "Persist")
$this->pop_parent->setIdSelf($arr);
}
/**
* Method used to load the object and all associations with the id value passed, or
* can receive an array of attributes and values it should return
*
* @param mixed $value Integer of the ID on database or other criterias as an array
* @return mixed
*/
public function loadAll($value)
{
$this->load($value);
foreach ($this->pop_attrs as $key=>$attr)
{
if (get_class($this->$key) == "PArrayOf")
{
$this->loadAllAssociated($key);
}
}
}
/**
* Method used to save the object on the database
*
* @return bool true on sucess, false and Exception on failure
*/
public function save()
{
//this must be called after checking id
//otherwise it could run the update instead
//of insert for new records
if ($this->pop_parent_type != "Persist")
$this->pop_parent->save();
//used to control if SQL should be executed or not in cases
//where object heritages from another but no self attribute
//is created
$control = false;
//if has PK, than update, else, insert
if (!$this->id_self && $this->getTargetName() != $this->pop_parent_schema.".".$this->pop_parent_table)
{
if ($this->pop_parent_type != "Persist")
$this->id->value = $this->pop_parent->id->value;
$sql = "insert into ".$this->getTargetName()." (";
//get current values to set to fields
$comma = "";
$values = "values (";
foreach ($this->pop_attrs as $key => $objvalue)
{
if ($this->checkAttrName($key) && get_class($objvalue)!= "PArrayOf")
{
$in_parent = false;
if ($key != "id" && $this->pop_parent_type != "Persist")
if ($this->checkParentAttrExistence($key))
$in_parent = true;
if ($key == "id" && $this->pop_parent_type == "Persist")
$in_parent = true;
if (!$in_parent)
{
$control = true;
$sql .= $comma.$this->getMetaName($key);
if ($objvalue || $objvalue === 0)
$value = $objvalue->value;
else
$value = "null";
$type = get_class($objvalue);
$values .= $comma.$this->pop_db_driver->filterInsert($value, $type, $objvalue->defaultval);
$comma = ", ";
}
}
}
$values .= ")";
$sql .= ") ".$values;
}
else
{
$sql = "";
if (count($this->pop_self_attrs))
{
$sql = "update ".$this->getTargetName()." set ";
$sql_id = "";
//get current values to set to fields
$comma = "";
foreach ($this->pop_attrs as $key => $objvalue)
{
if ($this->checkAttrName($key) && get_class($objvalue)!= "PArrayOf")
{
if ($key=="id")
{
if ($this->getTargetName() == $this->pop_parent_schema.".".$this->pop_parent_table)
{
$this->id->value = $this->pop_parent->id->value;
$this->id_self = $this->id->value;
}
$sql_id .= " where ".$this->getMetaName($key)." = ".$this->id->value;
}
else
{
$in_parent = false;
if ($this->checkParentAttrExistence($key))
$in_parent = true;
if (!$in_parent)
{
$control = true;
if ($objvalue || $objvalue === 0)
$value = $objvalue->value;
else
$value = "null";
$type = get_class($objvalue);
$value = $this->pop_db_driver->filterUpdate($value, $type, $objvalue->defaultval);
$sql .= $comma.$this->getMetaName($key)." = ".$value;
$comma = ", ";
}
}
}
}
$sql .= $sql_id;
}
}
if ($control)
{
try
{
if (POPEnvironment::$debug)
echo "<pre><font color=#009900>".$sql."</font></pre><br>";
$this->pop_db_driver->beginTransaction($this->pop_db);
POPDB::getConnection($this->pop_db)->exec($sql);
if (!$this->id->value)
{
$this->id->value = $this->pop_db_driver->getLastId($this->pop_db, $this->getParentTable(),$this->getMetaName("id"));
if (POPEnvironment::$debug)
echo "Last Insert Id: <font color=#FF0000>".$this->id->value."</font>";
}
$this->id_self = $this->id->value;
$this->pop_db_driver->commit($this->pop_db);
return true;
}
catch (Exception $e)
{
$this->pop_db_driver->rollBack($this->pop_db);
throw new Exception('Invalid SQL: <font color=#FF0000>"'.$sql.'"</font>'."\n". $e->getMessage());
}
}
}
/**
* Method used to save the object and all associations on the database
*/
public function saveAll()
{
$this->save();
foreach ($this->pop_attrs as $key=>$attr)
{
if (get_class($this->$key) == "PArrayOf")
{
$this->saveAllAssociated($key);
}
}
}
/**
* Method used to delete the object from the database
*/
public function delete()
{
if ($this->id->value)
{
$sql = "delete from ".$this->getTargetName()." where ".$this->getMetaName("id")." = ".$this->id->value;
try
{
if (POPEnvironment::$debug)
echo "<pre><font color=#009900>".$sql."</font></pre><br>";
$this->pop_db_driver->beginTransaction($this->pop_db);
POPDB::getConnection($this->pop_db)->exec($sql);
$this->pop_db_driver->commit($this->pop_db);
if ($this->pop_parent_type!="Persist")
$this->pop_parent->delete();
return true;
}
catch (Exception $e)
{
$this->pop_db_driver->rollBack($this->pop_db);
throw new Exception('Invalid SQL: <font color=#FF0000>"'.$sql.'"</font>'."\n". $e->getMessage());
}
}
else
{
throw new Exception("This object is not in the database yet.\nYou have to save it first to generate an id.");
}
}
/**
* Method used to add an object to an attribtute that is a PArrayOf
*
* @param string $associated Name of the attribute that holds the PArrayOf
* @param mixed $element Name of the attribute that holds the PArrayOf
*/
public function addAssociated($associated, $element)
{
$this->$associated->add($element);
}
/**
* Method used to delete an object from a collection hold on a PArrayOf attribute
*
* @param string $associated Name of the attribute that holds the PArrayOf
* @param string $criteria Criteria used ("index"||"id")
* @param integer $value Value of the criteria
*/
public function delAssociated($associated,$criteria,$value)
{
if($criteria == "index")
{
$index = $value;
if ($index < count($this->$associated->array) && $this->$associated->array[$index])
$this->$associated->array[$index]->delete();
else
{
throw new Exception("There is no object loaded on $associated with index: $index.");
}
}
else if($criteria == "id")
{
$exists = false;
foreach($this->$associated->array as $index => $obj)
{
if ($obj->id->value == $value)
{
$this->$associated->array[$index]->delete();
$exists = true;
}
}
if (!$exists)
{
throw new Exception("There is no object loaded on $associated with this id: $value.");
}
}
$this->$associated->del($criteria,$value);
}
/**
* Method used to get an object from a collection hold on a PArrayOf attribute
*
* @param string $associated Name of the attribute that holds the PArrayOf
* @param string $criteria Criteria used ("index"||"id")
* @param integer $value Value of the criteria
*/
public function &getAssociated($associated,$criteria,$value)
{
if($criteria == "index")
{
$index = $value;
if ($index < count($this->$associated->array) && $this->$associated->array[$index])
return $this->$associated->array[$index];
else
{
throw new Exception("There is no object loaded on $associated with index: $index.");
}
}
else if($criteria == "id")
{
foreach($this->$associated->array as $index => $obj)
if ($obj->id->value == $value)
return $this->$associated->array[$index];
//if it doens't find any, throw exception
throw new Exception("There is no object loaded on $associated with this id: $value.");
}
}
/**
* Internal Method used to get the name of an associated object
*
* @param object $obj Object used for the extraction
*/
public function getAssociatedName($obj)
{
if (in_array($this->pop_parent_type, $obj->parent_classes))
{
foreach ($obj->parent_classes as $classname)
{
if ($this->pop_parent_type == $classname)
return strtolower(get_class($this->pop_parent));
else
return strtolower(get_class($this));
}
}
else
{
if(in_array(get_class($this), $obj->parent_classes))
{
foreach ($obj->parent_classes as $classname)
return strtolower(get_class($this));
}
else
return strtolower($this->pop_parent->getAssociatedName($obj));
}
}
/**
* Method used to get load an object to a collection hold on by a PArrayOf attribute
*
* @param string $associated Name of the attribute that holds the PArrayOf
*/
public function loadAssociated($associated)
{
try
{
$obj = new $this->$associated->objecttype();
$associated_name = $this->getAssociatedName($obj);
$id = $obj->getMetaName("id_".$associated_name);
$id_value = $this->id->value;
if (!$this->id->value && $this->pop_parent->id->value)
$id_value = $this->pop_parent->id->value;
$sql = "select ".$obj->getMetaName("id")." as id from ".$this->pop_schema.".".$obj->pop_table." where $id = ".$id_value;
if (POPEnvironment::$debug)
echo "<pre><font color=#009900>".$sql."</font></pre><br>";
$rs = POPDB::getConnection($this->pop_db)->query($sql);
$result = $rs->fetchAll();
$rs->closeCursor();
$this->$associated->reset();
foreach ($result as $row)
{
$obj = new $this->$associated->objecttype();
$obj->load($row["id"]);
$this->$associated->add($obj);
}
}
catch (Exception $e)
{
throw new Exception("<pre><font color=#FF0000>Error: $attr<br>".$e->getMessage()."</font>");
}
}
/**
* Method used to get load all objects to a collection hold on by a PArrayOf attribute
*
* @param string $associated Name of the attribute that holds the PArrayOf
*/
public function loadAllAssociated($associated)
{
$this->loadAssociated($associated);
foreach ($this->$associated->array as $obj)
{
$obj->loadAll($obj->id->value);
}
}
/**
* Method used to get save all objects hold on a collection by a PArrayOf attribute
*
* @param string $associated Name of the attribute that holds the PArrayOf
*/
public function saveAssociated($associated)
{
if (!$this->id->value)
{
throw new Exception("This object is not in the database yet.\nYou have to save it first to generate an id.");
}
$obj = new $this->$associated->objecttype();
$associated_name = $this->getAssociatedName($obj);
$id = "id_".$associated_name;
foreach ($this->$associated->array as $obj)
{
foreach ($obj->parent_classes as $objparent)
{
$id_parent = "id_".strtolower($objparent);
if (!$obj->$id->value)
$obj->$id->value = $this->id->value;
if(!$obj->pop_attrs[$id_parent]->value)
{
if($this->$id_parent->value)
$obj->pop_attrs[$id_parent]->value = $this->$id_parent->value;
else
{
throw new Exception("ID not set.");
}
}
}
$obj->save();
}
}
/**
* Method used to get save all objects and associated objects hold on a collection
* by a PArrayOf attribute
*
* @param string $associated Name of the attribute that holds the PArrayOf
*/
public function saveAllAssociated($associated)
{
$obj = new $this->$associated->objecttype();
$associated_name = $this->getAssociatedName($obj);
$id = "id_".$associated_name;
foreach ($this->$associated->array as $obj)
{
foreach ($obj->parent_classes as $objparent)
if (isset($obj->$id) && !$obj->$id->value)
$obj->$id->value = $this->id->value;
$obj->saveAll();
}
}
/**
* Method used to get search objects on the database, given the criterias
*
* @param array $return_attributes Array listing the attributes/columns you want to be returned
* @param array $search_attributes_and_values Criterias used for searching
* @param string $output_format Format of the return. Can be "PDO" or "XML". Default is PDO.
* @return mixed PDO Recordset Object or XML string
*/
public function search($return_attributes = null, $search_attributes_and_values = null, $output_format = null)
{
//if ($return_attributes && $search_attributes_and_values)
{
$comma = "";
$sql = "select";
$tables = array();
$tables[$this->getTargetName()] = $this->getTargetName();
$sql .= "\n\t".$this->getTargetName().".".$this->getMetaName("id")." as id";
$comma = ",";
if (is_array($return_attributes))
{
foreach ($return_attributes as $field)
{
//first, we get our own metas
foreach ($this->pop_self_attrs as $key=>$value)
{
if ($field == $key)
{
if (get_class($this->$key) != "PArrayOf" && $this->checkAttrName($key))
{
$type = get_class($this->$key);
$sql .= $comma."\n\t".$this->pop_db_driver->filterSelect($this->getTargetName(), $this->getMetaName($key), $key, $type);
}
}
}
//now, we get our parents metas
$parents = $this->getParentMetaAttrs();
foreach ($parents as $table=>$attrs)
{
$tables[$table] = $table;
foreach ($attrs as $attr=>$meta)
{
if ($field == $attr)
{
if (get_class($this->$attr) != "PArrayOf" && $this->checkAttrName($attr))
{
$type = get_class(get_class($this->$attr));
$sql .= $comma."\n\t".$this->pop_db_driver->filterSelect($table, $meta, $attr, $type);
}
}
}
}
}
}
else// if($return_attributes == "*")
{
//first, we get our own metas
foreach ($this->pop_self_attrs as $key=>$value)
{
if (get_class($this->$key) != "PArrayOf" && $this->checkAttrName($key))
{
$type = get_class($this->$key);
$sql .= $comma."\n\t".$this->pop_db_driver->filterSelect($this->getTargetName(), $this->getMetaName($key), $key, $type);
}
}
//now, we get our parents metas
$parents = $this->getParentMetaAttrs();
foreach ($parents as $table=>$attrs)
{
$tables[$table] = $table;
foreach ($attrs as $attr=>$meta)
{
if (get_class($this->$attr) != "PArrayOf" && $this->checkAttrName($attr))
{
$type = get_class(get_class($this->$attr));
$sql .= $comma."\n\t".$this->pop_db_driver->filterSelect($table, $meta, $attr, $type);
}
}
}
}
//now, we "from" with left joins
$sql .= "\nfrom";
$sql_inner_joins = "";
$count = count($tables) - 1;
$i = 0;
foreach ($tables as $table)
{
if ($i==$count)
$sql_inner_joins = "\n\t".$table.$sql_inner_joins;
if ($i!=0)
$sql_inner_joins = str_replace("{next_table_id}",$table.".".$this->getMetaName("id"), $sql_inner_joins);
if ($i!=$count)
$sql_inner_joins = "\n\t\tinner join ".$table." on ".$table.".".$this->getMetaName("id")." = {next_table_id}".$sql_inner_joins;
$i++;
}
$sql .= $sql_inner_joins;
//now, we'll get by the selected attributes.
//now, we build the where clause
if (is_array($search_attributes_and_values))
{
//now, we "where"
$sql .= "\nwhere";
foreach ($search_attributes_and_values as $key => $paramvalue)
{
$in_parent = false;
$in_this = false;
//again, we must check what is not in the parent
if ($this->checkParentAttrExistence($key))
$in_parent = true;
if ($this->checkAttrExistence($key))
$in_this = true;
if (!$in_parent && $in_this || $key=="id")
{
if (is_array($paramvalue))
{
foreach($paramvalue as $value)
{
$sql .= "\n\t".$this->getTargetName().".".$this->getMetaName($key)." ".$value;
}
}
else
$sql .= "\n\t".$this->getTargetName().".".$this->getMetaName($key)." ".$paramvalue;
}
else
{
foreach ($parents as $table=>$attrs)
{
if (isset($attrs->$key))
{
if (is_array($paramvalue))
{
foreach($paramvalue as $value)
{
$sql .= "\n\t".$table.".".$this->getMetaName($key)." ".$value;
}
}
else
$sql .= "\n\t".$table.".".$this->getMetaName($key)." ".$paramvalue;
}
}
}
}
}
/****************still must check code************/
try
{
if (POPEnvironment::$debug)
echo "<pre><font color=#009900>".$sql."</font></pre><br>";
$rs = POPDB::getConnection($this->pop_db)->query($sql);
$result = $rs->fetchAll();
$rs->closeCursor();
if (!$output_format || ($output_format != "xml" && $output_format != "XML"))
return $result;
else if ($output_format == "xml" || $output_format == "XML")
{
$return = "<result>";
foreach ($result as $item)
{
$return .= "<item id=\"".$item["id"]."\">";
if ($return_attributes)
{
foreach ($return_attributes as $key)
{
$return .= "<".$key.">".$item[$key]."</".$key.">";
}
}
else
{
foreach ($this->pop_attrs as $key => $value)
{
if ($this->checkAttrName($key))
$return .= "<".$key.">".$item[$key]."</".$key.">";
}
}
$return .= "</item>";
}
$return .= "</result>";
return $return;
}
}
catch (Exception $e)
{
throw new Exception('Invalid SQL: <font color=#FF0000>"'.$sql.'"</font>'."\n". $e->getMessage());
}
}
}
/**
* Converts the array to a JSON String, useful on WebServices
*
* @param string $attr Array of the attributes you want to be converted. null means you want all of them
* @param string $tab Internal use - for identation
* @param string $endcomma Internal use - for identation
* @return string
*/
public function toJSON($attr=null, $tab = "", $endcomma="")
{
$comma = "";
$json = $tab."{";
foreach ($this->pop_attrs as $key => $objvalue)
{
if ($this->checkAttrName($key) && (!$attr || ($attr && in_array($key,$attr))))
{
if ($objvalue instanceof PArrayOf)
{
$json .= $comma."\n".$tab."\t".$key." : [\n";
$json .= $objvalue->toJSON($attr, $tab, $endcomma);
$json .= $tab."\t]";
}
else if (isset($objvalue))
$json .= $comma."\n".$tab."\t".$key ." : ".$objvalue->value;
$comma = ",";
}
}
$json .= "\n".$tab."}".$endcomma."\n}\n";
return $json;
}
/**
* Converts the array to a XML String, useful on WebServices
*
* @param string $attr Array of the attributes you want to be converted. null means you want all of them
* @return string
*/
public function toXML($attr=null)
{
$xml = "<".get_class($this)." id=\"".$this->id->value."\">";
foreach ($this->pop_attrs as $key => $objvalue)
{
if ($this->checkAttrName($key) && (!$attr || ($attr && in_array($key,$attr))))
{
if ($objvalue instanceof PArrayOf)
{
$xml .= "<".$key .">";
$xml .= $objvalue->toXML($attr);
$xml .= "</".$key .">";
}
else if (isset($objvalue))
$xml .= "<".$key .">". $objvalue->value . "</".$key .">";
}
}
$xml .= "</".get_class($this).">";
return $xml;
}
/**
* Drops the table associated to the object from the database
*/
public function dropTable()
{
try
{
$this->pop_db_driver->dropTable($this->pop_db, $this->pop_schema, $this->pop_table);
}
catch (Exception $e)
{
throw new Exception('Invalid SQL: <font color=#FF0000>"'.$sql.'"</font>'."\n". $e->getMessage());
}
}
/**
* Creates the table associated to the object on the database
*/
public function createTable()
{
$comma = "";
$sql = "";
$sql_fields = "";
foreach ($this->pop_attrs as $key => $objvalue)
{
if ( $this->checkAttrName($key) && get_class($objvalue)!= "PArrayOf")
{
$in_parent = false;
if ($key == "id" && $this->pop_parent_type == "Persist")
$sql_fields .= $comma."\n\t".$this->getMetaName("id").$this->pop_db_driver->identityField();
else
{
if ($this->checkParentAttrExistence($key))
$in_parent = true;
if ($key == "id")
$in_parent = false;
if (!$in_parent)
{
$type = strtolower(get_class($objvalue));
$type = substr($type,1,(strlen($type) - 1));
$sql_fields .= $comma."\n\t".$this->getMetaName($key)." ";
$size = null;
if (!is_null($objvalue->size))
$size = $objvalue->size;
$sql_fields .= $this->pop_db_driver->fieldCreation($type, $objvalue->defaultval, $size);
}
}
if (!$in_parent)
$comma = ",";
}
}
$sql .= "create table ".$this->getTargetName()."\n(";
if ($sql_fields=="")
$comma = "";
$sql .= $sql_fields.$comma."\n\tprimary key (".$this->getMetaName("id").")";
if ($this->parent_classes)
{
foreach($this->parent_classes as $parenttype)
{
if (!$this->checkParentAttrExistence($this->getMetaName("id_".strtolower($parenttype))))
{
$sql .= ",\n\tforeign key(".$this->getMetaName("id_".strtolower($parenttype)).")";
$parent_class_temp = new $parenttype();
if(count($this->pop_attrs['meta_mapping'])>0 || count($parent_class_temp->pop_attrs['meta_mapping'])>0)
{
$sql .= "\n\t\treferences ".strtolower($parent_class_temp->getTargetName())."(".$parent_class_temp->getMetaName("id").")";
}else
$sql .= "\n\t\treferences ".strtolower($parent_class_temp->getTargetName())."(id)";
$sql .= $this->pop_db_driver->createPropagation();
}
}
}
if ($this->pop_parent_type != "Persist")
{
//foreign key
$sql .= ",\n\tforeign key(".$this->getMetaName("id").")";
$sql .= "\n\t\treferences ".$this->pop_parent->getTargetName()."(".$this->pop_parent->getMetaName("id").")";
$sql .= $this->pop_db_driver->createPropagation();
$sql .= "\n)";
}
else
$sql .= "\n)\n";
if(POPEnvironment::$engine)
$sql .= "ENGINE=".POPEnvironment::$engine;
$sql .= $this->pop_db_driver->sequenceCreation($this->getTargetName());
try
{
//first, try to create the schema (if necessary)
$this->pop_db_driver->createSchema($this->pop_db,$this->pop_schema);
$this->pop_db_driver->beginTransaction($this->pop_db);
POPDB::getConnection($this->pop_db)->exec($sql);
$this->pop_db_driver->commit($this->pop_db);
if (POPEnvironment::$debug)
echo "<pre><font color=#009900>".$sql."</font></pre><br>";
return true;
}
catch (Exception $e)
{
$this->pop_db_driver->rollBack($this->pop_db);
throw new Exception('Invalid SQL: <font color=#FF0000>"'.$sql.'"</font>'."\n". $e->getMessage());
}
}
/**
* Alters the table associated to the object on the database to the current object spacification.
* If the table doesn't exists, it tries to create it.
*/
public function alterTable()
{
}
/**
* Check if the attribute can be used, or if it's an internal attribute
*
* @return bool
*/
protected function checkAttrName($key)
{
if (
$key != "id_self" &&
$key != "pop_db" &&
$key != "pop_dbtype" &&
$key != "pop_parent" &&
$key != "pop_parent_type" &&
$key != "pop_attrs" &&
$key != "pop_parent_attrs" &&
$key != "pop_self_attrs" &&
$key != "pop_schema" &&
$key != "pop_table" &&
$key != "pop_parent_schema" &&
$key != "pop_parent_table" &&
$key != "pop_db_driver" &&
$key != "parent_classes" &&
$key != "schema" &&
$key != "table" &&
$key != "meta_mapping"
)
return true;
return false;
}
/**
* Get the table name of the parent object
*
* @return string
*/
protected function getParentTable()
{
if ($this->pop_parent_type != "Persist")
{
return $this->pop_parent->getParentTable();
}
else
return $this->getTargetName();
}
/**
* Check if the attribute exists with that name
*
* @return bool
*/
protected function checkAttrExistence($key)
{
foreach ($this->pop_attrs as $var => $objvalue)
if ($key == $var)
return true;
return false;
}
/**
* Check if the attribute exists with that name on the parent
*
* @return bool
*/
protected function checkParentAttrExistence($key)
{
foreach ($this->pop_parent_attrs as $var => $parobjvalue)
if ($key == $var)
return true;
return false;
}
/**
* Get the name of the attribute from the meta mapping
*
* @return string
*/
public function getMetaName($key)
{
$key = strtolower($key);
if ($this->meta_mapping && isset($this->meta_mapping[$key]))
return $this->meta_mapping[$key];
else
{
if($this->checkParentAttrExistence($key))
return $this->pop_parent->getMetaName($key);
else
return $key;
}
}
/**
* Get the name of the field from the meta mapping
*
* @return string
*/
public function getRealName($meta)
{
$meta = strtolower($meta);
if ($this->meta_mapping && in_array($meta,$this->meta_mapping))
return array_search($meta,$this->meta_mapping);
else
return $meta;
}
/**
* Set the PDO DB connection that should be used by the object and get default properties
* from the driver, like default schema.
*
* @param string $pop_db name of the global used for database connection
*/
public function setDBConnection()
{
if ($this->db)
$this->pop_db = $this->db;
POPDB::getConnection($this->pop_db)->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pop_dbtype = POPDB::getConnection($this->pop_db)->getAttribute(PDO::ATTR_DRIVER_NAME);
$driver = POPDBDriverRegistry::getDriver($this->pop_dbtype);
$this->pop_db_driver = new $driver($this->pop_db);
}
/**
* Get the PDO DB connection name in use
*
* @return string
*/
public function getDBConnection()
{
return $this->pop_db;
}
/**
* Get the name of the schema from the meta mapping
*
* @return string
*/
protected function getSchemaName()
{
if ($this->schema)
$this->pop_schema = $this->schema;
else
$this->schema = $this->pop_schema = strtolower($this->pop_db_driver->schema);
return $this->pop_schema;
}
/**
* Get the name of the table from the meta mapping
*
* @return string
*/
protected function getTableName()
{
if ($this->table)
$this->pop_table = $this->table;
else
$this->table = $this->pop_table = strtolower(get_class($this));
return $this->pop_table;
}
/**
* Get the target name (schema + table)
*/
public function getTargetName()
{
if ($this->pop_schema)
return $this->pop_schema.$this->pop_db_driver->separator.$this->pop_table;
else
return $this->pop_table;
}
/**
* Get the attributes mapped on parent
*
* @return mixed
*/
public function getParentMetaAttrs($arr = array())
{
if ($this->pop_parent_type != "Persist")
{
if (!isset($arr[$this->pop_parent->getTargetName()]))
$arr[$this->pop_parent->getTargetName()] = array();
$temp_arr = $this->pop_parent->pop_self_attrs;
foreach ($temp_arr as $key=>$value)
if ($this->pop_parent->checkAttrName($key))
$arr[$this->pop_parent->getTargetName()][$key] = $this->pop_parent->getMetaName($key);
return $this->pop_parent->getParentMetaAttrs($arr);
}
else
return $arr;
}
/**
* Initializes the class
*/
protected function initialize()
{
//object will use the Global PDO DB connection created before
$this->setDBConnection();
//get the schema name to be used
$this->getSchemaName();
//get the table name to be used
$this->getTableName();
$this->pop_attrs = get_object_vars($this);
$this->pop_parent_attrs = array();
$this->pop_self_attrs = array();
//if has parent and it is not Persist, we must have a way to get it
$this->pop_parent_type = get_parent_class($this);
if ($this->pop_parent_type != "Persist")
{
$this->pop_parent = new $this->pop_parent_type();
$this->pop_parent_attrs = get_object_vars($this->pop_parent);
$this->pop_parent_table = strtolower($this->pop_parent->getTableName());
$this->pop_parent_schema = strtolower($this->pop_parent->getSchemaName());
if ($this->pop_parent->parent_classes == $this->parent_classes)
$this->parent_classes = array();
}
$this->pop_self_attrs = array_diff_key($this->pop_attrs, $this->pop_parent_attrs,array("id"=>""));
}
}
?>