* @category class * @package core * @subpackage api * @copyright 2005-2017 Titan Framework * @license http://www.titanframework.com/license/ BSD License (3 Clause) * @see Form, Api, ApiAuth, ApiException, ApiList * @link http://www.titanframework.com/docs/api/ * @todo Create Item class for array replace. */ class ApiEntity { protected $file = ''; protected $primary = ''; protected $itemId = 0; protected $codeColumn = ''; protected $code = NULL; protected $table = ''; public $fields = array (); public function __construct () { $section = Business::singleton ()->getSection (Section::TCURRENT); $action = Business::singleton ()->getAction (Action::TCURRENT); $args = func_get_args(); $fileName = FALSE; if ($action->getXmlPath () !== FALSE && trim ($action->getXmlPath ()) != '') array_unshift ($args, $action->getXmlPath ()); foreach ($args as $trash => $arg) { if (!file_exists ('section/'. $section->getName () .'/'. $arg)) continue; $fileName = $arg; break; } if ($fileName === FALSE) throw new Exception ('Arquivo XML não encontrado em [section/'. $section->getName () .'/].'); $file = 'section/'. $section->getName () .'/'. $fileName; $cacheFile = Instance::singleton ()->getCachePath () .'parsed/'. fileName ($file) .'_'. md5_file ($file) .'.php'; if (file_exists ($cacheFile)) $array = include $cacheFile; else { $xml = new Xml ($file); $array = $xml->getArray (); if (!isset ($array ['api'][0])) throw new Exception ('A tag <api></api> não foi encontrada no XML ['. $fileName .']!'); xmlCache ($cacheFile, $array); } if (!array_key_exists ('api', $array)) throw new Exception ('Invalid XML View file [section/'. $section->getName () .'/].'); $array = $array ['api'][0]; $this->file = $fileName; if (array_key_exists ('table', $array)) $this->table = trim ($array ['table']); if (array_key_exists ('primary', $array)) $this->primary = trim ($array ['primary']); if (array_key_exists ('code', $array)) $this->codeColumn = trim ($array ['code']); $user = User::singleton (); if (array_key_exists ('field', $array) && is_array ($array ['field'])) foreach ($array ['field'] as $trash => $field) if ($obj = Type::factory ($this->getTable (), $field)) $this->fields [$obj->getAssign ()] = $obj; reset ($this->fields); } public function getFile () { return $this->file; } public function getTable () { return $this->table; } public function setTable ($table) { $this->table = $table; } public function getPrimary () { return $this->primary; } public function getCodeColumn () { return $this->codeColumn; } public function useCode () { return $this->getCodeColumn () != ''; } public function getFields () { return $this->fields; } public function getId () { return $this->itemId; } public function setId ($itemId) { $this->itemId = $itemId; } public function getCode () { return $this->code; } public function setCode ($code) { $this->code = $code; } public function getField ($assign = FALSE) { if ($assign !== FALSE) if (array_key_exists ($assign, $this->fields)) return $this->fields [$assign]; else return NULL; $field = each ($this->fields); if ($field !== FALSE) return $field ['value']; reset ($this->fields); return NULL; } public function takeOutField ($assign) { if (!array_key_exists ($assign, $this->fields)) return NULL; $out = $this->fields [$assign]; unset ($this->fields [$assign]); return $out; } public function getFieldByColumn ($column) { foreach ($this->fields as $assign => $field) if ($field->getColumn () == $column) return $field; return NULL; } public function recovery ($data = FALSE) { if (is_array ($data)) foreach ($data as $key => $value) { if (is_string ($value)) $data [$key] = $value; } else $data = $_POST; foreach ($this->fields as $assign => $field) { if ($field->isReadOnly ()) continue; if (!array_key_exists ($field->getApiColumn (), $data) && !array_key_exists ($field->getApiColumn (), $_FILES)) { if ($field->isRequired ()) throw new ApiException (__ ('Required field [[1]] is missing or empty!', $field->getLabel ()), ApiException::ERROR_INVALID_PARAMETER, ApiException::BAD_REQUEST); unset ($this->fields [$assign]); continue; } if (array_key_exists ($field->getApiColumn (), $_FILES)) $value = $_FILES [$field->getApiColumn ()]; else $value = $data [$field->getApiColumn ()]; $this->fields [$assign]->setValue (self::fromApi ($this->fields [$assign], $value)); if (!$this->fields [$assign]->isValid ()) throw new ApiException (__ ('The value of field [[1]] is invalid!', $field->getLabel ()), ApiException::ERROR_INVALID_PARAMETER, ApiException::BAD_REQUEST); } return TRUE; } public function load ($id, $ownerOnly = FALSE, $user = NULL) { $fields = array (); foreach ($this->fields as $assign => $field) if ($field->isLoadable ()) $fields [] = Database::toSql ($field); if ($ownerOnly && (is_null ($user || !is_integer ($user) || !$user))) throw new Exception (__ ('The user must be acknowledged! Please, alert system responsible.')); $db = Database::singleton (); if ($this->useCode ()) { if (is_null ($id) || trim ($id) == '') throw new Exception (__ ('Invalid code!')); $this->setCode ($id); if ($ownerOnly) $sql = "SELECT ". $this->getPrimary () .", ". implode (', ', $fields) ." FROM ". $this->getTable () ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn () ." AND _user = :_user"; else $sql = "SELECT ". $this->getPrimary () .", ". implode (', ', $fields) ." FROM ". $this->getTable () ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn (); $sth = $db->prepare ($sql); $sth->bindParam (':'. $this->getCodeColumn (), $id, PDO::PARAM_STR); } else { if (is_null ($id) || !is_integer ($id) || !(int) $id) throw new Exception (__ ('Invalid identifier!')); if ($ownerOnly) $sql = "SELECT ". $this->getPrimary () .", ". implode (', ', $fields) ." FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = :". $this->getPrimary () ." AND _user = :_user"; else $sql = "SELECT ". $this->getPrimary () .", ". implode (', ', $fields) ." FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = :". $this->getPrimary (); $sth = $db->prepare ($sql); $sth->bindParam (':'. $this->getPrimary (), $id, PDO::PARAM_INT); } if ($ownerOnly) $sth->bindParam (':_user', $user, PDO::PARAM_INT); $sth->execute (); $obj = $sth->fetch (PDO::FETCH_OBJ); if (!$obj) return FALSE; $pk = $this->getPrimary (); $itemId = $obj->$pk; $this->setId ($itemId); foreach ($this->fields as $assign => $field) if ($field->isLoadable ()) $this->fields [$assign] = Database::fromDb ($field, $obj); elseif (isset ($itemId)) $this->fields [$assign]->load ($itemId); reset ($this->fields); return TRUE; } public function save ($user, $id = NULL) { $mandatory = Database::getMandatoryColumns ($this->getTable ()); $_change = $this->getFieldByColumn ('_change'); if (!in_array ('_change', $mandatory) || is_null ($_change) || !is_object ($_change) || !$_change->getUnixTime ()) throw new ApiException (__ ('Has a problem with column of last change control! Please, alert system responsible.'), ApiException::ERROR_SYSTEM, ApiException::INTERNAL_SERVER_ERROR); $fields = array (); $values = array (); $binds = array (); $types = array (); $sizes = array (); $mandatoryForServerOnly = array ('_create', '_update', '_user'); foreach ($this->fields as $key => $field) { if (in_array ($field->getColumn (), $mandatoryForServerOnly)) continue; if ($field->isReadOnly () || !$field->isSavable ()) continue; $assign = $field->getAssign (); $fields [$assign] = $field->getColumn (); if ($field->getBind ()) { $values [$assign] = ":". $field->getColumn (); $binds [$assign] = Database::toBind ($field); $types [$assign] = $field->getBindType (); if (method_exists ($field, 'getMaxLength')) $sizes [$assign] = (int) $field->getMaxLength (); else $sizes [$assign] = 0; } else $values [$assign] = Database::toValue ($field); } if (in_array ('_user', $mandatory)) { $fields [] = '_user'; $values [] = (int) $user; } if (in_array ('_update', $mandatory)) { $fields [] = '_update'; $values [] = 'NOW()'; } foreach ($mandatory as $trash => $column) $$column = $this->getFieldByColumn ($column); // throw new Exception ($itemId); $db = Database::singleton (); $aux = array (); foreach ($fields as $key => $field) $aux [] = $field ." = ". $values [$key]; if ($this->useCode ()) { if (is_null ($id) || trim ($id) == '') throw new ApiException (__ ('Invalid code!'), ApiException::ERROR_SYSTEM, ApiException::INTERNAL_SERVER_ERROR); $sqlUpdate = "UPDATE ". $this->getTable () ." SET ". implode (", ", $aux) ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn () ." AND ". $_change->getUnixTime () ." > extract (epoch from _change)::integer"; $sthUpdate = $db->prepare ($sqlUpdate); $iFields = $fields; $iValues = array (); foreach ($fields as $key => $field) if (array_key_exists ($key, $binds)) $iValues [$key] = ":". $field ." AS ". $field; else $iValues [$key] = $values [$key] ." AS ". $field; if (in_array ('_author', $mandatory) && !isset ($_author)) { $iFields [] = '_author'; $iValues [] = (int) $user ." AS _author"; } if (in_array ('_devise', $mandatory) && !isset ($_devise)) { $iFields [] = '_devise'; $iValues [] = Database::toValue ($_change) ." AS _devise"; } $sqlInsert = "INSERT INTO ". $this->getTable () ." (". $this->getCodeColumn () .", ". implode (", ", $iFields) .") SELECT (:". $this->getCodeColumn () .")::varchar AS ". $this->getCodeColumn () .", ". implode (", ", $iValues) ." WHERE NOT EXISTS (SELECT 1 FROM ". $this->getTable () ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn () .")"; $sthInsert = $db->prepare ($sqlInsert); $sthUpdate->bindParam (':'. $this->getCodeColumn (), $id, PDO::PARAM_STR); $sthInsert->bindParam (':'. $this->getCodeColumn (), $id, PDO::PARAM_STR); foreach ($binds as $assign => $trash) if ($sizes [$assign] && $types [$assign] == PDO::PARAM_STR) { $sthUpdate->bindParam ($values [$assign], $binds [$assign], $types [$assign], $sizes [$assign]); $sthInsert->bindParam ($values [$assign], $binds [$assign], $types [$assign], $sizes [$assign]); } else { $sthUpdate->bindParam ($values [$assign], $binds [$assign], $types [$assign]); $sthInsert->bindParam ($values [$assign], $binds [$assign], $types [$assign]); } $sthUpdate->execute (); $sthInsert->execute (); $sth = $db->prepare ("SELECT ". $this->getPrimary () ." AS pk FROM ". $this->getTable () ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn ()); $sth->bindParam (':'. $this->getCodeColumn (), $id, PDO::PARAM_STR); $sth->execute (); $itemId = (int) $sth->fetchColumn (); $this->setCode ($id); } else if (!is_null ($id) && is_numeric ($id) && (int) $id) { $id = (int) $id; $sqlUpdate = "UPDATE ". $this->getTable () ." SET ". implode (", ", $aux) ." WHERE ". $this->getPrimary () ." = :". $this->getPrimary () ." AND ". $_change->getUnixTime () ." > extract (epoch from _change)::integer"; $sth = $db->prepare ($sqlUpdate); $sth->bindParam (':'. $this->getPrimary (), $id, PDO::PARAM_INT); foreach ($binds as $assign => $trash) if ($sizes [$assign] && $types [$assign] == PDO::PARAM_STR) $sth->bindParam ($values [$assign], $binds [$assign], $types [$assign], $sizes [$assign]); else $sth->bindParam ($values [$assign], $binds [$assign], $types [$assign]); $sth->execute (); $itemId = $id; } else { $id = (int) Database::nextId ($this->getTable (), $this->getPrimary ()); $iFields = $fields; $iValues = $values; if (in_array ('_author', $mandatory) && !isset ($_author)) { $iFields [] = '_author'; $iValues [] = (int) $user; } if (in_array ('_devise', $mandatory) && !isset ($_devise)) { $iFields [] = '_devise'; $iValues [] = Database::toValue ($_change); } $sth = $db->prepare ("INSERT INTO ". $this->getTable () ." (". $this->getPrimary () .", ". implode (", ", $iFields) .") VALUES (:". $this->getPrimary () .", ". implode (", ", $iValues) .")"); $sth->bindParam (':'. $this->getPrimary (), $id, PDO::PARAM_INT); foreach ($binds as $assign => $trash) if ($sizes [$assign] && $types [$assign] == PDO::PARAM_STR) $sth->bindParam ($values [$assign], $binds [$assign], $types [$assign], $sizes [$assign]); else $sth->bindParam ($values [$assign], $binds [$assign], $types [$assign]); $sth->execute (); $itemId = $id; } if (!isset ($itemId) || is_null ($itemId) || !$itemId) return FALSE; $this->setId ($itemId); Lucene::singleton ()->save ($itemId, $this->getResume ($itemId, TRUE)); foreach ($this->fields as $key => $field) if (!$field->isSavable ()) $field->save ($itemId); reset ($this->fields); return $itemId; } public function delete ($id = NULL, $permanent = TRUE, $ownerOnly = FALSE, $user = NULL) { if ($ownerOnly && (is_null ($user || !is_integer ($user) || !$user))) throw new Exception (__ ('The user must be acknowledged! Please, alert system responsible.')); $db = Database::singleton (); if ($this->useCode ()) { if (is_null ($id) || trim ($id) == '') throw new Exception (__ ('Invalid code!')); if ($permanent) if ($ownerOnly) $sql = "DELETE FROM ". $this->getTable () ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn () ." AND _user = :_user"; else $sql = "DELETE FROM ". $this->getTable () ." WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn (); else if ($ownerOnly) $sql = "UPDATE ". $this->getTable () ." SET _deleted = B'1' WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn () ." AND _user = :_user"; else $sql = "UPDATE ". $this->getTable () ." SET _deleted = B'1' WHERE ". $this->getCodeColumn () ." = :". $this->getCodeColumn (); $sth = $db->prepare ($sql); $sth->bindParam (':'. $this->getCodeColumn (), $id, PDO::PARAM_STR); } else { if (is_null ($id) || !is_integer ($id) || !(int) $id) throw new Exception (__ ('Invalid identifier!')); if ($permanent) if ($ownerOnly) $sql = "DELETE FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = :". $this->getPrimary () ." AND _user = :_user"; else $sql = "DELETE FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = :". $this->getPrimary (); else if ($ownerOnly) $sql = "UPDATE ". $this->getTable () ." SET _deleted = B'1' WHERE ". $this->getPrimary () ." = :". $this->getPrimary () ." AND _user = :_user"; else $sql = "UPDATE ". $this->getTable () ." SET _deleted = B'1' WHERE ". $this->getPrimary () ." = :". $this->getPrimary (); $sth = $db->prepare ($sql); $sth->bindParam (':'. $this->getPrimary (), $id, PDO::PARAM_INT); } if ($ownerOnly) $sth->bindParam (':_user', $user, PDO::PARAM_INT); if (!$sth->execute ()) return FALSE; Lucene::singleton ()->delete ($itemId); return TRUE; } public function getResume ($friendly = FALSE) { $resume = ""; if (!$friendly) { $resume .= "[ID# ". $this->getId () ." / CODE: ". $this->getCode () ." ] \n\n"; } while ($field = $this->getField (FALSE)) $resume .= (trim ($field->getLabel ()) != '' ? $field->getLabel () .": \n" : '') . Form::toText ($field) ." \n\n"; reset ($this->fields); return $resume; } public function json () { $itemId = $this->getId (); $object = array (); if ($this->useCode ()) $object [$this->getCodeColumn ()] = $this->getCode (); else $object [$this->getPrimary ()] = $itemId; while ($field = $this->getField ()) $object [$field->getApiColumn ()] = ApiEntity::toApi ($field, $itemId); return json_encode ((object) $object); } public static function getNextKey ($array) { $key = 0; while (array_key_exists ($key, $array)) $key++; return $key; } public static function toApi ($field, $itemId = NULL) { if (!is_object ($field)) return $field; $instance = Instance::singleton (); $fieldId = 'field_'. $field->getAssign (); $db = Database::singleton (); $type = get_class ($field); do { $file = $instance->getTypePath ($type) .'toApi.php'; if (file_exists ($file)) return include $file; $type = get_parent_class ($type); } while ($type != 'Type' && $type !== FALSE); $type = get_class ($field); do { $file = $instance->getTypePath ($type) .'toText.php'; if (file_exists ($file)) return include $file; $type = get_parent_class ($type); } while ($type != 'Type' && $type !== FALSE); $type = get_class ($field); do { $file = $instance->getTypePath ($type) .'toHtml.php'; if (file_exists ($file)) return strip_tags (include $file); $type = get_parent_class ($type); } while ($type != 'Type' && $type !== FALSE); return strip_tags ($field->getValue ()); } public static function fromApi ($field, $value) { if (!is_object ($field)) return $value; $instance = Instance::singleton (); $type = get_class ($field); do { $file = $instance->getTypePath ($type) .'fromApi.php'; if (file_exists ($file)) return include $file; $type = get_parent_class ($type); } while ($type != 'Type' && $type !== FALSE); $type = get_class ($field); do { $file = $instance->getTypePath ($type) .'fromForm.php'; if (file_exists ($file)) return include $file; $type = get_parent_class ($type); } while ($type != 'Type' && $type !== FALSE); return $value; } }