. if (!defined('EG')) die('Direct access not allowed!'); class Model_Tree extends Model_Base { public function __construct() { parent::__construct(); } // //method to create the first part of where clause // //$index: the index of $this->_tablesArray // public function createTreeWhere($index) { // if (!empty($this->on)) // { // return $this->on; // } // else // { // $whereArray = array(); // for ($i = $index; $i < (count($this->_tablesArray)-1); $i++) // { // $whereArray[] = $this->_tablesArray[$i].'.'.$this->_idFieldsArray[$i+1].'='.$this->_tablesArray[$i+1].'.'.$this->_idFieldsArray[$i+1]; // } // $whereString = !empty($whereArray) ? implode(' and ',$whereArray) : null; // return $whereString; // } // } // //create the list of tables of the select query // //$index: the index of $this->_tablesArray // public function createTablesList($index) { // if (isset($this->from)) // { // return $this->from; // } // else // { // $tablesString = null; // for ($i = $index; $i < (count($this->_tablesArray)-1); $i++) // { // $tablesString .= $this->_tablesArray[$i] . ','; // } // $tablesString .= $this->_tablesArray[count($this->_tablesArray)-1]; // return $tablesString; // } // } // // //method to create the list of fields of the select query // public function createFieldsList($index) { // $fieldsString = null; // for ($i = $index; $i < (count($this->_tablesArray)-1); $i++) // { // $fieldsString .= $this->_tablesArray[$i] . '.*,'; // } // $fieldsString .= $this->_tablesArray[count($this->_tablesArray)-1].'.*'; // return $fieldsString; // } //method to create the where clause and the list of tables and fields of the select query //$tableName: the table name ($this->_tablesArray) //$choice:all->all the tables in $this->_arrayTables, other value->only the table of $this->_arrayTables ad index $index //return: $elements = array('tables'=>$tables,'where'=>$where,'fields'=>$fields) public function treeQueryElements($tableName,$choice = 'all') { // $index = array_search($tableName,$this->_tablesArray); // $subArray = ($choice === 'all') ? array_slice($this->_tablesArray,$index) : array($tableName); //this array is necessary to verify that the where clause makes sense // $tables = ($choice === 'all') ? $this->createTablesList($index) : $tableName; // $where = ($choice === 'all') ? $this->createTreeWhere($index) : null; // $fields = ($choice === 'all') ? $this->createFieldsList($index) : $tableName.'.*'; // // $wherePlus = $this->createWhereClause(); // // if (empty($this->on)) // { // $on = array(); // // if (isset($where) and isset($wherePlus)) // { // $where .= ' AND ' . $wherePlus; // } // else if (!isset($where) and isset($wherePlus)) // { // $where .= $wherePlus; // } // } // else // { // $on = $where; // $where = $wherePlus; // } $where = $this->createWhereClause(); if (isset($where)) { if (isset($this->sWhere)) { $where .= " AND " . $this->sWhere; } } else { $where = $this->sWhere; } $tables = isset($this->from) ? $this->from : $tableName; $fields = $tableName.".*"; $on = $this->on; return array('tables' => $tables,'where' => $where,'fields'=>$fields,'on'=>$on); } //method to obtain the values of the whole tree //$choice:all->all the tables in $this->_arrayTables, other value->only the table of $this->_arrayTables ad index $index public function getAll($choice = 'all') { return $this->getFields('',$choice); } //method to get the values of the selected fields //it walks the tree by means of a join query //$fields: the fields that have to be excracted from the tableName public function getFields($fields = '',$choice = 'all') { $elements = $this->treeQueryElements($this->_tablesArray[0],$choice); $queryFields = (strcmp($fields,'') === 0) ? $elements['fields'] : $fields; $row = $this->db->select($elements['tables'],$queryFields,$elements['where'],$this->groupBy,$this->orderBy,$this->limit,$elements['on'],$this->using,$this->join); //convert from MySQL values if ($this->convert) { if (count($row) > 0) { $types = array(); $tablesList = array_keys($row[0]); foreach ($tablesList as $table) { $types[$table] = $this->db->getTypes($table, "*", false, true); } for ($i=0;$i< count ($row); $i++) { foreach ($tablesList as $table) { $row[$i][$table] = $this->convertFromMysqlT($types[$table], $row[$i][$table]); } } } } return $row; } public function send() { $table = $this->getFields($this->select); if ($this->toList) { $key = $this->listArray[0]; $value = isset($this->listArray[1]) ? $this->listArray[1] : null; $this->toList = false; return $this->getList($table, $key, $value); } else { return $table; } } //call the getAll method with $tableName = $this->_tablesArray[0] //the fields that have to be extracted from the table public function getTable($fields = null) { return isset($fields) ? $this->getFields($fields) : $this->getAll(); } //select the values of a specified record //$id: the id (primary key) of the record //$fields: the comma separated list of fields that have to be extracted public function selectId($id,$fields = null) { $this->save()->clear()->setWhereQueryClause(array($this->_idFieldsArray[0] => (int)$id)); $this->using = null; if (isset($fields)) { $values = $this->getFields($fields,'other'); } else { $values = $this->getAll('other'); } $this->restore(); return (count($values) > 0) ? $values[0][$this->_tablesArray[0]] : array(); } //get the number of records () //the number of records of the table $tableName is returned public function rowNumber() { $elements = $this->treeQueryElements($this->_tablesArray[0]); return $this->db->get_num_rows($elements['tables'],$elements['where'],$this->groupBy,$elements['on'],$this->using,$this->join); } public function getMax($field) { $elements = $this->treeQueryElements($this->_tablesArray[0]); return $this->db->getMax($elements['tables'],$field,$elements['where'],$this->groupBy,$elements['on'],$this->using,$this->join); } public function getMin($field) { $elements = $this->treeQueryElements($this->_tablesArray[0]); return $this->db->getMin($elements['tables'],$field,$elements['where'],$this->groupBy,$elements['on'],$this->using,$this->join); } public function getSum($field) { $elements = $this->treeQueryElements($this->_tablesArray[0]); return $this->db->getSum($elements['tables'],$field,$elements['where'],$this->groupBy,$elements['on'],$this->using,$this->join); } public function getAvg($field) { $elements = $this->treeQueryElements($this->_tablesArray[0]); return $this->db->getAvg($elements['tables'],$field,$elements['where'],$this->groupBy,$elements['on'],$this->using,$this->join); } //check if the table has the field $field equal to $value public function has($field,$value) { $elements = $this->treeQueryElements($this->_tablesArray[0]); return $this->db->recordExists($elements['tables'],$field,$value,$elements['where'],$this->groupBy,$elements['on'],$this->using,$this->join); } //check referential integrity during delete public function checkOnDeleteIntegrity($id = null, $whereClause = null) { $this->save(); $result = true; if (count($this->foreignKeys) > 0) { foreach ($this->foreignKeys as $f) { if (preg_match('/^(.*?)\s(parent of)\s(.*?)\((.*?)\)(\s(on delete)\s(cascade|restrict)\s\((.*?)\))?$/i',$f,$matches)) { $parentKey = $matches[1]; $childModel = $matches[3]; $childField = $matches[4]; if (isset($whereClause)) { $this->clear()->sWhere($whereClause); } else { $this->clear()->where(array($this->_idFields=>(int)$id)); } $keys = sanitizeDbDeep($this->toList($parentKey)->send()); $this->restore(); if (count($keys) > 0) { $child = new $childModel(); $childrenIds = $child->clear()->where(array($childField=>"in('".implode("','",$keys)."')"))->toList($child->getPrimaryKey())->send(); if (count($childrenIds) > 0) { if (isset($matches[7]) and strcmp($matches[7],"cascade") === 0) { foreach ($childrenIds as $childId) { $child->del((int)$childId); } if (strcmp($matches[8],"") !== 0) { $this->notice .= "
".$matches[8]."
"; } } else { $this->notice .= isset($matches[8]) ? "
".$matches[8]."
" : $this->_resultString->getString('associate'); $result = false; } } } } } } return $result; } //check referential integrity during insert or update public function checkOnUpdateIntegrity($queryType) { $result = true; if (count($this->foreignKeys) > 0) { foreach ($this->foreignKeys as $f) { if (preg_match('/^(.*?)\s(child of)\s(.*?)\((.*?)\)(\s(on update)\s(restrict)\s\((.*?)\))?$/i',$f,$matches)) { $childKey = $matches[1]; $ParentModel = $matches[3]; $ParentField = $matches[4]; $notice = isset($matches[8]) ? "
".$matches[8]."
" : ""; if (array_key_exists($childKey,$this->values)) { $parent = new $ParentModel(); $res = $parent->clear()->where(array($ParentField=>sanitizeDb($this->values[$childKey])))->send(); if (count($res) === 0) { $this->notice .= $notice; $this->result = false; $result = false; } } else if ($queryType === "insert") { $this->notice .= $notice; $this->result = false; $result = false; } } } } return $result; } //get the first extracted full record public function record() { $res = $this->getFields($this->select); if (count($res) > 0) { return $res[0][$this->_tables]; } return array(); } //get a single field from the first extracted record public function field($fieldName) { $res = $this->save()->select($fieldName)->send(); $this->restore(); if (count($res) > 0 and isset($res[0][$this->_tables][$fieldName])) { return $res[0][$this->_tables][$fieldName]; } return ""; } //get the types of the fields in $this->values public function getTypes($full = false) { return $types = $this->db->getTypes($this->_tables,implode(",",array_keys($this->values)),$full); } //automatically set the values conditions public function setValuesConditionsFromDbFields($queryType) { $fields = array_keys($this->values); $fieldsAsString = implode(",",$fields); $types = $this->getTypes(true); $fieldKeys = $this->db->getKeys($this->_tables,$fieldsAsString,true,false); if (count($this->values) > 0) { if (!$types) { $this->notice .= $this->_resultString->getString('not-existing-fields'); $this->result = false; return false; } else { $this->saveConditions("values"); $this->saveConditions("database"); if (Params::$setValuesConditionsFromDbTableStruct) { foreach ($types as $index => $t) { if (preg_match('/^('.implode("|",$this->db->getCharTypes()).')\(([0-9]*?)\)$/i',$t,$matches)) { $this->addValuesCondition($queryType,'checkLength|'.$matches[2],$fields[$index]); } else if (preg_match('/^('.implode("|",$this->db->getIntegerTypes()).')/i',$t,$matches)) { $this->addValuesCondition($queryType,'checkInteger',$fields[$index]); } else if (preg_match('/^('.implode("|",$this->db->getFloatTypes()).')$/i',$t,$matches)) { $this->addValuesCondition($queryType,'checkNumeric',$fields[$index]); } else if (preg_match('/^('.implode("|",$this->db->getDateTypes()).')$/i',$t,$matches)) { $this->addValuesCondition($queryType,'checkIsoDate',$fields[$index]); } else if (preg_match('/^('.implode("|",$this->db->getEnumTypes()).')\((.*?)\)$/i',$t,$matches)) { $temp = array(); $strings = explode(",",$matches[2]); for ($i=0;$iaddValuesCondition($queryType,'checkIsStrings|'.implode(",",$temp),$fields[$index]); } else if (preg_match('/^('.implode("|",$this->db->getDecimalTypes()).')\((.*?)\)$/i',$t,$matches)) { $this->addValuesCondition($queryType,'checkDecimal|'.$matches[2],$fields[$index]); } } //set unique conditions foreach ($fieldKeys as $index => $fk) { if (preg_match('/^('.implode("|",$this->db->getUniqueIndexStrings()).')$/i',$fk,$matches)) { if ($queryType === "insert") { $this->addDatabaseCondition($queryType,'checkUnique',$fields[$index]); } else { $this->addDatabaseCondition($queryType,'checkUniqueCompl',$fields[$index]); } } } } foreach (Params::$valuesConditionsFromFormatsOfFieldsNames as $regExpr => $function) { foreach ($fields as $f) { if (preg_match($regExpr,$f,$matches)) { $this->addValuesCondition($queryType,$function,$f); } } } } } // print_r($fields); // print_r($types); return true; } //convert values of the $this->values to MySQL formats public function convertValuesToDb() { if (Params::$automaticConversionToDbFormat) { if (isset($this->_conversionToDbObject)) { $types = $this->getTypes(); if ($types) { $fields = array_keys($this->values); foreach ($types as $index => $t) { if (method_exists($this->_conversionToDbObject,strtolower($t))) { $this->values[$fields[$index]] = call_user_func(array($this->_conversionToDbObject, strtolower($t)), $this->values[$fields[$index]]); } } } } } } public function insert() { $this->db->setAutocommit(true); $this->notice = null; $this->queryResult = false; $this->convertValuesToDb(); if ($this->checkOnUpdateIntegrity("insert")) { //set the values conditions from the table description if ($this->setValuesConditionsFromDbFields("insert")) { if ($this->applyDatabaseConditions("insert",null)) { $this->restoreConditions("database"); if ($this->applyValidateConditions("insert",'values')) { $this->restoreConditions("values"); return parent::insert(); } $this->restoreConditions("values"); } $this->restoreConditions("database"); } } return false; } public function update($id = null, $whereClause = null) { $this->db->setAutocommit(true); $this->notice = null; $this->queryResult = false; $this->convertValuesToDb(); if ($this->checkOnUpdateIntegrity("update")) { //set the values conditions from the table description if ($this->setValuesConditionsFromDbFields("update")) { if (!isset($id) or $this->applyDatabaseConditions("update",(int)$id)) { $this->restoreConditions("database"); //check the values conditions if ($this->applyValidateConditions("update",'values')) { $this->restoreConditions("values"); return parent::update($id, $whereClause); } $this->restoreConditions("values"); } $this->restoreConditions("database"); } } return false; } //method to call the delete query (overriding of the del method of Model.php) //check the referential integrity public function del($id = null, $whereClause = null) { $this->notice = null; $this->queryResult = false; if ($this->checkOnDeleteIntegrity($id, $whereClause)) { return parent::del($id, $whereClause); } else { return false; } // if (isset($whereClause)) // { // return parent::del(null,$whereClause); // } // else // { // if ($this->_onDelete === 'check' and isset($this->_reference)) // { // if (isset($this->_reference[0]) and isset($this->_reference[1])) // { // if ($this->db->recordExists($this->_reference[0],$this->_reference[1],(int)$id)) // { // $this->notice = $this->_resultString->getString('associate'); // $this->identifierValue = null; // $this->result = false; // } // else // { // return parent::del((int)$id); // } // } // else // { // throw new Exception('you have forgotten to set \'$this->_reference\' or you have forgotten to set $this->_onDelete = \'nocheck\''); // } // } // else // { // return parent::del((int)$id); // } // } // return false; } //method to obtain one columns from the tables $this->_tablesArray as an associative array //$valueField: the column that have to be extracted (array_values of the resulting associative array), $keyField: the column that have to play the role of array_keys public function getFieldArray($valueField,$keyField = null, $groupBy = null, $orderBy = null, $limit = null) { $keyField = isset($keyField) ? $keyField : $valueField; $valueFieldArray = explode(':',$valueField); $keyFieldArray = explode(':',$keyField); $keyFieldTable = $keyFieldArray[0]; $valueFieldTable = $valueFieldArray[0]; $keyFieldName = $keyFieldArray[1]; $valueFieldName = $valueFieldArray[1]; $fields = implode('.',$keyFieldArray) . ',' . implode('.',$valueFieldArray); $temp = $this->where; //save the $this->where array $this->where = array(); if (strcmp($keyFieldTable,$valueFieldTable) !== 0) { throw new Exception("the tables '$valueFieldTable' and '$keyFieldTable' do not match in ".__METHOD__); } if (!in_array($keyFieldTable,$this->_tablesArray)) { throw new Exception("the table '$keyFieldTable' is not allowed in ".__METHOD__); } $elements = $this->treeQueryElements($keyFieldTable,''); $table = $this->db->select($elements['tables'],$fields,$elements['where'],$groupBy,$orderBy,$limit,$elements['on'],$this->using); $this->where = $temp; $returnArray = array(); foreach ($table as $record) { $returnArray[$record[$keyFieldTable][$keyFieldName]] = $record[$valueFieldTable][$valueFieldName]; } return $returnArray; } }