diff --git a/README.md b/README.md index 60dd3af..62acce1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,198 @@ # DBmodel -This library extends the builtin PDO object by several useful features. \ No newline at end of file +This library extends the builtin PDO object by several useful features. + +## Basic usage + +For testing create database `test` with user `test` and password `test`. In database create table `test` and insert same testing data. + +``` + +CREATE TABLE `test` ( + `id` int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `created` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +``` + +For basic testing run this code + +``` +query("show tables"); +print_r($result); + +// test results +$result = $db->getAll("show databases"); +print_r($result); + +$result = $db->getAll("show tables"); +print_r($result); + +$result = $db->getCol("show tables"); +print_r($result); + +$result = $db->getTableColumns("test"); +print_r($result); + +$result = $db->getRow("select * from test where id = 10"); +print_r($result); + + +?> +``` + +## Interactive create MODEL class + +Create file `creatorModel.php` and paste bellow code + +``` +interact(); + + +?> +``` + +now for each table run `php createModel.php` for interactive creating class for table. + +``` +$ php test2.php +New model name: Test +Table name: test +Entity name: test +Create MODEL class '/your/project/models/Test.php'? (y - yes, other - no) y +Create directory '/your/project/models'? (y - yes, other - no) y +Creating MODEL class ... MODEL class created +``` + +Creator build this MODEL class file + +``` + array( + 'name' => 'test', + 'primary_key_name' => 'id', + 'allow_attributes' => array( + 'name' => 'varchar(255)', + 'created' => 'datetime' + ) + ), + ); + + public function exist($primary_key = null) { + return $this->existRecord('test', $primary_key); + } + + public function test($primary_key = null, $data = array()) { + return $this->record('test', $primary_key, $data); + } + + public function testBy($colname, $colvalue) { + return $this->recordBy('test', $colname, $colvalue); + } + + public function testSave($data = array()) { + return $this->test($this->exist($data) ? $data : null, $data); + } + + public function testEmpty() { + return $this->recordEmpty('test'); + } + + public function testAttributes() { + return $this->typesAttributes('test'); + } + + public function testCount() { + return $this->count('test'); + } + + public function getList($search = array(), $reverse = false, $concat_or = false) { + return $this->search('test') + ->where($search, $concat_or) + ->order(array('id' => $reverse ? 'DESC' : 'ASC')) + ->toArray(); + } + + public function testCombo($col_key, $col_value, $add_empty = false) { + return $this->search('test') + ->toCombo($col_key, $col_value, $add_empty); + } + +} + +?> + +``` + +## Using MODEL class in your project + +Illustrative example + +``` +exist(11); +print_r($result); + +// insert new record +$result = $test->test(null, array('name' => 'new record')); +print_r($result); + +// update record ID 11 +$result = $test->test(11, array('name' => 'updated record')); +print_r($result); + +// get record ID 11 +$result = $test->test(11); +print_r($result); + +// delete record ID 11 +$result = $test->test(11, null); +print_r($result); + +// get all records +$result = $test->getList(); +print_r($result); + +// get combined list of ID and name +$result = $test->testCombo('id', 'name'); +print_r($result); + +?> +``` diff --git a/src/DBmodel.php b/src/DBmodel.php new file mode 100644 index 0000000..cc0fc86 --- /dev/null +++ b/src/DBmodel.php @@ -0,0 +1,1453 @@ + + License: GNU GPL-3.0 and later + + Description: This library extends the builtin PDO object by several useful features. + + Milestones: + 2015-10-09 23:52 Igor - Created for Beteha + 2015-12-01 23:09 Igor - Added LEFT a RIGHT JOIN, added flag DEBUG, userd for print of query + 2016-07-28 09:44 Igor - Added return of associative array + 2025-05-27 20:43 Igor - Forked from Beteha to standalone public library +*/ + +namespace TPsoft\DBmodel; + +class DBmodel +{ + public static DBmodel $instance; + public \PDO $dbh; + public \PDOStatement $stmt; + private $type = 'mysql'; + private $oldErrorHandler; + public $debug = false; + public $log = null; + public $tables = array(); + + public function __construct(string $dsn = null, ?string $username = null, #[\SensitiveParameter] ?string $password = null, ?array $options = null) + { + if (is_null($dsn)) { + if (DBmodel::$instance) { + $this->dbh = DBmodel::$instance->dbh; + } else { + throw new \Exception('DB handler from DBmodel::$instance is null'); + } + } else { + $this->dbh = new \PDO($dsn, $username, $password, $options); + DBmodel::$instance = $this; + } + if (is_null($this->dbh)) { + throw new \Exception('DB handler is null'); + } + } + + public function _debug($msg) + { + if (!is_null($this->log)) { + call_user_func_array($this->log, array($msg)); + } + if ((is_bool($this->debug) && $this->debug == true) + || (is_numeric($this->debug) && $this->debug-- <= 0) + ) { + throw new \Exception($msg); + } + } + + /* ---------------------------------------------------- + * CORE METHODS + */ + public function tableInfoExist($table_id, $info) + { + return (isset($this->tables) + && isset($this->tables[$table_id]) + && isset($this->tables[$table_id][$info])); + } + + public function tableInfo($table_id, $info) + { + if ( + !isset($this->tables) + || !isset($this->tables[$table_id]) + || !isset($this->tables[$table_id][$info]) + ) { + throw new \Exception('Error: Incorrect table "' . $info . '" for table ID "' . $table_id . '"'); + } + return $this->tables[$table_id][$info]; + } + + public function tableInfoSet($table_id, $info, $content) + { + $this->tables[$table_id][$info] = $content; + } + + public function tableName($table_id) + { + return $this->tableInfo($table_id, 'name'); + } + + public function primaryKeyName($table_id) + { + return $this->tableInfo($table_id, 'primary_key_name'); + } + + public function allowAttributes($table_id) + { + $allow_attributes = $this->tableInfo($table_id, 'allow_attributes'); + if (count($allow_attributes) <= 0) return array(); + $keys = array_keys($allow_attributes); + return (strlen($keys[0]) <= 1) ? $allow_attributes : array_keys($allow_attributes); + } + + public function typesAttributes($table_id) + { + if ($this->tableInfoExist($table_id, 'allow_attributes_types')) { + return $this->tableInfo($table_id, 'allow_attributes_types'); + } + $allow_attributes = $this->tableInfo($table_id, 'allow_attributes'); + if (count($allow_attributes) <= 0) return false; + $keys = array_keys($allow_attributes); + if (strlen($keys[0]) <= 1) { + $this->tableInfoSet($table_id, 'allow_attributes_types', false); + return false; + } + $types = array(); + foreach ($allow_attributes as $key => $type) { + $type = str_replace(' ', '', $type); + $type = str_replace('(', '|', $type); + $type = str_replace(')', '|', $type); + $type_parts = explode('|', $type); + $rettype = array( + 'format' => strtolower(@$type_parts[0]), + 'length' => @$type_parts[1] + ); + if (strtolower($rettype['format']) == 'enum') { + $allowed_values = explode(',', $rettype['length']); + foreach ($allowed_values as $index => $part) $allowed_values[$index] = trim($part, '"\''); + $rettype['allowed_values'] = $allowed_values; + $rettype['length'] = count($allowed_values); + } + $types[$key] = $rettype; + } + $this->tableInfoSet($table_id, 'allow_attributes_types', $types); + return $types; + } + + private function buildWherePrimaryKey($table_id, $primary_key) + { + $primary_key_name = $this->primaryKeyName($table_id); + if (is_array($primary_key_name)) { + if (!is_array($primary_key)) { + throw new \Exception('Error: table ' . $table_id . ' required multikey select'); + } + $conds = array(); + foreach ($primary_key_name as $name) { + $conds[] = sprintf('`%s` = %s', $name, $this->quote($primary_key[$name])); + } + $where = implode(' AND ', $conds); + } else { + $pk = is_array($primary_key) ? $primary_key[$primary_key_name] : $primary_key; + $where = sprintf('`%s` = %s', $primary_key_name, $this->quote($pk)); + } + return $where; + } + + public function existTable($table_id) + { + $query = sprintf( + 'SHOW TABLES LIKE "%s"', + $this->tableName($table_id) + ); + return $this->getOne($query) == $this->tableName($table_id); + } + + public function existRecord($table_id, $primary_key) + { + if ( + is_null($primary_key) + || (is_string($primary_key) && strlen(trim($primary_key)) <= 0) + ) { + return false; + } + $query = sprintf( + 'SELECT COUNT(*)' + . ' FROM %s' + . ' WHERE %s', + $this->tableName($table_id), + $this->buildWherePrimaryKey($table_id, $primary_key) + ); + return $this->getOne($query) == 1; + } + + public function record($table_id, $primary_key = null, $data = array()) + { + $action = null; + $action = (!is_null($primary_key) && is_array($data) && count($data) <= 0) ? 'SELECT' : $action; + $action = (is_null($primary_key) && is_array($data) && count($data) > 0) ? 'INSERT' : $action; + $action = (!is_null($primary_key) && is_array($data) && count($data) > 0) ? 'UPDATE' : $action; + $action = (!is_null($primary_key) && is_null($data)) ? 'DELETE' : $action; + if (is_null($action)) { + $this->_debug('[DBmodel][record]: action = null'); + return false; + } + $table = $this->tableName($table_id); + $primary_key_name = $this->primaryKeyName($table_id); + $allow_attributes = $this->allowAttributes($table_id); + $data = $this->fixDecimalPoints($table_id, $data); + $data = $this->fixBooleans($table_id, $data); + $data = $this->fixJSONs($table_id, $data); + $data = $this->fixTypes($table_id, $data); + + if ($action == 'SELECT') { + if (!$this->existRecord($table_id, $primary_key)) { + return false; + } + $query = sprintf( + 'SELECT *' + . ' FROM %s' + . ' WHERE %s' + . ' LIMIT 1', + $table, + $this->buildWherePrimaryKey($table_id, $primary_key) + ); + $this->_debug('[DBmodel][record][SELECT]: ' . $query); + $ret = $this->getRow($query); + $ret = $this->unfixJSONs($table_id, $ret); + if ($ret === false) { + throw new \Exception('Error: unfixJSONs failed'); + } + return $ret; + } + + if ($action == 'INSERT') { + // check all required primary keys + $primary_key_names = is_array($primary_key_name) ? $primary_key_name : array($primary_key_name); + foreach ($primary_key_names as $pk_name) { + if ( + in_array($pk_name, $allow_attributes) + && (!isset($data[$pk_name]) + || is_null($data[$pk_name]) + || strlen($data[$pk_name]) <= 0) + ) { + $this->_debug('[DBmodel][record][INSERT]: missing primary key in data'); + return false; + } + } + $keys = ''; + $values = ''; + $delimiter = ''; + foreach ($data as $key => $value) { + $key = trim($key); + if (!in_array($key, $allow_attributes)) { + continue; + } + if (is_array($value)) { + $value = implode(',', array_keys($value)); + } + $keys .= $delimiter . '`' . $key . '`'; + if (is_string($value) && $value === '`NOW`') $value = date('Y-m-d H:i:s'); + $values .= (strlen($value) > 0) ? $delimiter . $this->quote($value) : $delimiter . 'NULL'; + $delimiter = ','; + } + $query = sprintf( + 'INSERT INTO %s' + . ' (%s)' + . ' VALUES' + . ' (%s)', + $table, + $keys, + $values + ); + $this->_debug('[DBmodel][record][INSERT]: ' . $query); + $ret = $this->query($query); + if ($ret === false) { + throw new \Exception($this->errorMessage()); + } + if ($this->affectedRows() != 1) { + return false; + } + if (count($primary_key_names) > 1) return true; + if (method_exists($this->dbh, 'getLastInsertID')) { + $last_id = $this->getLastInsertID(); + } else { + $last_id = $this->getOne('SELECT last_insert_id()'); + } + return $last_id; + } + + if ($action == 'UPDATE') { + if (!$this->existRecord($table_id, $primary_key)) { + if (is_array($primary_key)) { + return $this->record($table_id, null, array_merge($data, $primary_key)); + } + $this->_debug('Record with primary key = ' . json_encode($primary_key) . ' was not exists'); + return false; + } + $set = ''; + $delimiter = ''; + foreach ($data as $key => $value) { + $key = trim($key); + if (!in_array($key, $allow_attributes)) { + continue; + } + if (is_array($value)) { + $value = implode(',', array_keys($value)); + } + $q_val = (strlen($value) > 0) ? $this->quote($value) : 'NULL'; + $q_val = (substr($value, 0, 1) == '_' && substr($value, -1) == '_') ? substr($value, 1, -1) : $q_val; + $q_val = (is_string($value) && $value === '`NOW`') ? $this->quote(date('Y-m-d H:i:s')) : $q_val; + $set .= $delimiter . '`' . $key . '` = ' . $q_val; + $delimiter = ','; + } + $query = sprintf( + 'UPDATE %s' + . ' SET %s' + . ' WHERE %s' + . ' LIMIT 1', + $table, + $set, + $this->buildWherePrimaryKey($table_id, $primary_key) + ); + $this->_debug('[DBmodel][record][UPDATE]: ' . $query); + $ret = $this->query($query); + if ($ret === false) { + throw new \Exception($this->errorMessage()); + } + /* data is not always updated, if the same data is updated + if ($this->affectedRows() != 1) { + $this->_debug('Error during uprade: ' . $query); + return false; + } + */ + return $primary_key; + } + + if ($action == 'DELETE') { + if (!$this->existRecord($table_id, $primary_key)) { + $this->_debug('Record with primary key = "' . $primary_key . '" was not exists'); + return false; + } + $query = sprintf( + 'DELETE FROM %s' + . ' WHERE %s' + . ' LIMIT 1', + $table, + $this->buildWherePrimaryKey($table_id, $primary_key) + ); + $this->_debug('[DBmodel][record][DELETE]: ' . $query); + $ret = $this->query($query); + if ($ret === false) { + throw new \Exception($this->errorMessage()); + } + if ($this->affectedRows() != 1) { + return false; + } + return true; + } + return false; + } + + public function recordBy($table_id, $colname, $colvalue) + { + if ( + is_null($colname) || is_null($colvalue) + || !is_string($colname) || strlen(trim($colname)) <= 0 + || !is_string($colvalue) || strlen(trim($colvalue)) <= 0 + ) { + return false; + } + $data = array($colname => $colvalue); + $data = $this->fixDecimalPoints($table_id, $data); + $data = $this->fixBooleans($table_id, $data); + $data = $this->fixJSONs($table_id, $data); + $data = $this->fixTypes($table_id, $data); + if (!is_array($data) || count($data) <= 0) return false; + $allow_attributes = $this->allowAttributes($table_id); + $where = ''; + $delimiter = ''; + foreach ($data as $key => $value) { + $key = trim($key); + if (!in_array($key, $allow_attributes)) continue; + if (is_array($value)) $value = implode(',', array_keys($value)); + $q_val = (strlen($value) > 0) ? $this->quote($value) : 'NULL'; + $q_val = (substr($value, 0, 1) == '_' && substr($value, -1) == '_') ? substr($value, 1, -1) : $q_val; + $q_val = (is_string($value) && $value === '`NOW`') ? $this->quote(date('Y-m-d H:i:s')) : $q_val; + $where .= $delimiter . '`' . $key . '` = ' . $q_val; + $delimiter = ' AND '; + } + $query = sprintf( + 'SELECT *' + . ' FROM %s' + . ' WHERE %s', + $this->tableName($table_id), + $where + ); + $this->_debug('[DBmodel][recordBy]: ' . $query); + $ret = $this->getRow($query); + $ret = $this->unfixJSONs($table_id, $ret); + return $ret; + } + + public function recordEmpty($table_id) + { + $allow = $this->allowAttributes($table_id); + if (!is_array($allow)) { + return false; + } + $ret = array(); + foreach ($allow as $a) { + $ret[$a] = null; + } + return $ret; + } + + public function count($table_id) + { + $query = sprintf( + 'SELECT COUNT(*)' + . ' FROM %s', + $this->tableName($table_id) + ); + return $this->getOne($query); + } + + public function search($table_id, $tab_hash = 'tab') + { + $qb = new QueryBuilder($this); + return $qb->beginAction('SELECT', $table_id, $tab_hash); + } + + public function insert($table_id, $tab_hash = 'tab') + { + $qb = new QueryBuilder($this); + return $qb->beginAction('INSERT', $table_id, $tab_hash); + } + + public function update($table_id, $tab_hash = 'tab') + { + $qb = new QueryBuilder($this); + return $qb->beginAction('UPDATE', $table_id, $tab_hash); + } + + public function delete($table_id, $tab_hash = 'tab') + { + $qb = new QueryBuilder($this); + return $qb->beginAction('DELETE', $table_id, $tab_hash); + } + + public function optimize($table_id) + { + $query = sprintf('OPTIMIZE TABLE `%s`', $this->tableName($table_id)); + $this->_debug('[DBmodel][optimize]: ' . $query); + $ret = $this->query($query); + if ($ret === false) { + throw new \Exception($this->errorMessage()); + } + return true; + } + + public function toGroupBy($arr, $col_name) + { + $ret = array(); + foreach ($arr as $row) { + if (!isset($ret[$row[$col_name]])) { + $ret[$row[$col_name]] = array(); + } + $ret[$row[$col_name]][] = $row; + } + return $ret; + } + + /* ---------------------------------------------------- + * BASIC DB METHODS + */ + public function query(string $query, ?int $fetch_mode = null, ?int $colno = null): bool|\PDOStatement + { + $this->_debug('[DBmodel][query]: ' . $query); + if ($fetch_mode == \PDO::FETCH_COLUMN) { + if (is_null($colno)) $colno = 0; + $this->stmt = $this->dbh->query($query, $fetch_mode, $colno); + } else { + $this->stmt = $this->dbh->query($query, $fetch_mode); + } + return $this->stmt; + } + + public function getOne(string $query): string|false + { + $result = $this->query($query, \PDO::FETCH_NUM); + if ($result === false) { + return false; + } + $row = $result->fetch(\PDO::FETCH_NUM); + if (!is_array($row) || count($row) <= 0) return false; + return $row[0]; + } + + public function getRow(string $query): array|false + { + $result = $this->query($query, \PDO::FETCH_ASSOC); + if ($result === false) { + return false; + } + $row = $result->fetch(\PDO::FETCH_ASSOC); + return $row; + } + + public function getCol(string $query): array|false + { + $result = $this->query($query, \PDO::FETCH_COLUMN); + if ($result === false) { + return false; + } + $return = []; + while ($row = $result->fetchColumn()) { + $return[] = $row; + } + return $return; + } + + public function getAll(string $query): array|false + { + $result = $this->query($query, \PDO::FETCH_ASSOC); + if ($result === false) { + return false; + } + return $result->fetchAll(\PDO::FETCH_ASSOC); + } + + public function affectedRows(): int + { + if ( + $this->stmt === false + || $this->stmt === null + ) + return 0; + return $this->stmt->rowCount(); + } + + public function quote(string $value): string + { + return $this->dbh->quote($value); + } + + public function getLastInsertID(?string $name = null): ?string + { + if ($this->type === 'pgsql') { + $id = $this->dbh->query('SELECT LASTVAL()')->fetchColumn(); + return (string) $id ?: null; + } + return $this->dbh->lastInsertId($name); + } + + public function errorMessage(): string + { + return implode(':', $this->dbh->errorInfo()); + } + + public function getTableColumns($table_name) { + $desc = $this->getAll(sprintf('DESC %s', $table_name)); + $desc = $this->arrayKeysToLowerCase($desc); + $pks = array(); + $columns = array(); + foreach ($desc as $d) { + $colname = strtolower($d['field']); + $types[$colname] = $d['type']; + if ($d['key'] == 'PRI') { + $pks[] = $colname; + if (strtolower($d['extra']) != 'auto_increment') { + $columns[] = $colname; + } + } else { + $columns[] = $colname; + } + } + return array('pks' => $pks, 'columns' => $columns, 'types' => $types); + } + + /* ---------------------------------------------------- + * HELPER METHODS + */ + + public function fixTypes($table_id, $data, $where_prefixes = array()) + { + $types = $this->typesAttributes($table_id); + if ($types === false || !is_array($data)) return $data; + $data_keys = array_keys($data); + if (is_array($types)) foreach ($types as $column => $type) { + if (!in_array($column, $data_keys)) continue; + if (is_null($data[$column])) continue; + // kontrola special + $val = $data[$column]; + if (is_array($val)) continue; + $prefix = $this->extractPrefix($val, $where_prefixes); + $n_val = str_replace($prefix, '', $val); + if (substr($n_val, 0, 1) == '`' && substr($n_val, -1) == '`') continue; + // kontrola formatu + $max_length = null; + $min_number = null; + $max_number = null; + $allowed_values = null; + switch (strtolower($type['format'])) { + case 'varchar': + $max_length = intval($type['length']); + break; + case 'tinyblob': + case 'tinytext': + $max_length = 255; + break; + case 'text': + case 'blob': + $max_length = 65535; + break; + case 'mediumblob': + case 'mediumtext': + $max_length = 16777216; + break; + case 'longblob': + case 'longtext': + $max_length = 4294967296; + break; + case 'tinyint': + $min_number = -128; + $max_number = 128; + break; // 1B + case 'smallint': + $min_number = -32768; + $max_number = 32768; + break; // 2B + case 'mediumint': + $min_number = -8388608; + $max_number = 8388608; + break; // 3B + case 'integer': + case 'int': + $min_number = -2147483648; + $max_number = 2147483648; + break; // 4B + case 'bigint': + $min_number = -1 * pow(2, 63); + $max_number = pow(2, 63); + break; // 8B + case 'decimal': + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $prefix = $this->extractPrefix($val, $where_prefixes); + $data[$column] = $prefix . $this->fixDecimalMysql($val, $type['length']); + } + } else { + $prefix = $this->extractPrefix($data[$column], $where_prefixes); + $data[$column] = $prefix . $this->fixDecimalMysql($data[$column], $type['length']); + } + break; + case 'float': + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $prefix = $this->extractPrefix($val, $where_prefixes); + $data[$column][$key] = $prefix . $this->fixDecimalPoint(floatval($this->fixDecimalPoint($val))); + } + } else { + $prefix = $this->extractPrefix($data[$column], $where_prefixes); + $data[$column] = $prefix . $this->fixDecimalPoint(floatval($this->fixDecimalPoint($data[$column]))); + } + break; + case 'enum': + $allowed_values = $type['allowed_values']; + break; + case 'date': + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $prefix = $this->extractPrefix($val, $where_prefixes); + $n_val = str_replace($prefix, '', $val); + $data[$column] = $prefix . date('Y-m-d', strtotime($n_val)); + } + } else { + $prefix = $this->extractPrefix($data[$column], $where_prefixes); + $n_val = str_replace($prefix, '', $data[$column]); + $data[$column] = $prefix . date('Y-m-d', strtotime($n_val)); + } + break; + case 'datetime': + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $prefix = $this->extractPrefix($val, $where_prefixes); + $n_val = str_replace($prefix, '', $val); + $data[$column] = $prefix . date('Y-m-d H:i:s', strtotime($n_val)); + } + } else { + $prefix = $this->extractPrefix($data[$column], $where_prefixes); + $n_val = str_replace($prefix, '', $data[$column]); + $data[$column] = $prefix . date('Y-m-d H:i:s', strtotime($n_val)); + } + break; + } + if (!is_null($max_length)) { + if (strlen($data[$column]) > $max_length) { + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $data[$column][$key] = mb_substr($val, 0, $max_length); + } + } else { + $data[$column] = mb_substr($data[$column], 0, $max_length); + } + } + } + if (!is_null($min_number)) { + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $prefix = $this->extractPrefix($val, $where_prefixes); + $n_val = str_replace($prefix, '', $val); + $data[$column][$key] = $prefix . max(intval($n_val), $min_number); + } + } else { + $prefix = $this->extractPrefix($data[$column], $where_prefixes); + $n_val = str_replace($prefix, '', $data[$column]); + $data[$column] = $prefix . max(intval($n_val), $min_number); + } + } + if (!is_null($max_number)) { + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + $prefix = $this->extractPrefix($val, $where_prefixes); + $n_val = str_replace($prefix, '', $val); + $data[$column][$key] = $prefix . min(intval($n_val), $max_number); + } + } else { + $prefix = $this->extractPrefix($data[$column], $where_prefixes); + $n_val = str_replace($prefix, '', $data[$column]); + $data[$column] = $prefix . min(intval($n_val), $max_number); + } + } + if (!is_null($allowed_values)) { + if (is_array($data[$column])) { + foreach ($data[$column] as $key => $val) { + if (!in_array($val, $allowed_values)) $data[$column][$key] = null; + } + } else { + if (!in_array($data[$column], $allowed_values)) $data[$column] = null; + } + } + } + return $data; + } + + public function fixDecimalMysql($num, $length) + { + $lengths = explode(',', $length); + $lengths_0 = intval($lengths[0]); + $lengths_1 = intval($lengths[1]); + $num = $this->fixDecimalPoint($num); + $num_parts = explode('.', $num); + if (!is_array($num_parts)) return 0; + if (count($num_parts) == 0) return intval($num); + if (count($num_parts) == 1) return intval($num_parts[0]); + $num = intval(substr($num_parts[0], 0, $lengths_0 - $lengths_1)) . '.' . $this->allowedChars(substr($num_parts[1] . str_repeat('0', $lengths_1), 0, $lengths_1), '0123456789'); + return $num; + } + + public function fixDecimalPoint($num) + { + return str_replace(',', '.', $num); + } + + public function fixDecimalPoints($table_id, $data) + { + if (!$this->tableInfoExist($table_id, 'fix_decimal_points')) return $data; + $for_fix = $this->tableInfo($table_id, 'fix_decimal_points'); + if (is_array($data)) foreach ($data as $key => $val) { + if (in_array($key, $for_fix)) { + $data[$key] = $this->fixDecimalPoint($val); + } + } + return $data; + } + + public function fixBoolean($val) + { + return in_array(strtolower($val), array('on', 'true', '1', 'yes', 'ano', 'checked', 'selected')) ? 1 : 0; + } + + public function fixBooleans($table_id, $data) + { + if (!$this->tableInfoExist($table_id, 'fix_booleans')) return $data; + $for_fix = $this->tableInfo($table_id, 'fix_booleans'); + if (is_array($data)) foreach ($data as $key => $val) { + if (in_array($key, $for_fix)) { + $data[$key] = $this->fixBoolean($val); + } + } + return $data; + } + + public function forceBooleans($table_id, &$data) + { + if (!$this->tableInfoExist($table_id, 'fix_booleans')) return $data; + $for_fix = $this->tableInfo($table_id, 'fix_booleans'); + if (is_array($for_fix)) foreach ($for_fix as $key) { + $data[$key] = $this->fixBoolean($data[$key]); + } + return $data; + } + + public function fixJSON($val) + { + return json_encode($val); + } + + public function fixJSONs($table_id, $data) + { + if (!$this->tableInfoExist($table_id, 'fix_json')) return $data; + $for_fix = $this->tableInfo($table_id, 'fix_json'); + if (is_array($data)) foreach ($data as $key => $val) { + if (in_array($key, $for_fix)) { + $data[$key] = $this->fixJSON($val); + } + } + return $data; + } + + public function unfixJSON($val) + { + if (!is_string($val)) return $val; + try { + $decoded = json_decode($val, true); + return $decoded; + } catch (\Exception $e) { + return $val; + } + } + + public function unfixJSONs($table_id, $data) + { + if (!$this->tableInfoExist($table_id, 'fix_json')) return $data; + $for_fix = $this->tableInfo($table_id, 'fix_json'); + if (is_array($data)) foreach ($data as $key => $val) { + if (in_array($key, $for_fix)) { + $data[$key] = $this->unfixJSON($val); + } + } + return $data; + } + + public function preventOneError() + { + $this->oldErrorHandler = set_error_handler(array($this, 'silentErrorHandler')); + } + + public function allowNextError() + { + if (is_null($this->oldErrorHandler)) { + return false; + } + set_error_handler($this->oldErrorHandler); + $this->oldErrorHandler = null; + return true; + } + + public function silentErrorHandler($errno, $errstr, $errfile, $errline) + { + return $this->allowNextError(); + } + + public function import($objModel) + { + if (is_null($objModel)) return false; + if ( + !isset($objModel->tables) + || !is_array($objModel->tables) + ) + return false; + $this->tables = array_merge($this->tables, $objModel->tables); + return is_array($this->tables); + } + + public function textSearch($find, $table_id = null) + { + if (is_null($table_id)) { + $ret = array(); + if (is_array($this->tables)) foreach ($this->tables as $t_id => $table_settings) { + $ret = array_merge($ret, $this->textSearch($find, $t_id)); + } + return $ret; + } + $allow_attributes = $this->tableInfo($table_id, 'allow_attributes'); + if (!is_array($allow_attributes) || count($allow_attributes) <= 0) return array(); + $keys = array_keys($allow_attributes); + if (strlen($keys[0]) <= 1) { // stary format bez typov + $table_columns = $this->getTableColumns($this->tableName($table_id)); + $attributes = $table_columns['types']; + } else { // novy format s typmi + $attributes = $allow_attributes; + } + if (!is_array($attributes) || count($attributes) <= 0) return array(); + $forsearch = array(); + foreach ($attributes as $name => $type) { + if ($this->checkPrefix(array('varchar', 'tinyblob', 'tinytext', 'text', 'blob', 'mediumblob', 'mediumtext', 'longblob', 'longtext'), $type, true)) { + $forsearch[] = $name; + } + } + if (count($forsearch) <= 0) return array(); + foreach ($forsearch as $colname) $conds[] = sprintf('`%s` LIKE %s', $colname, $this->quote('%' . $find . '%')); + $query = sprintf('SELECT * FROM %s WHERE %s', $this->tableName($table_id), implode(' OR ', $conds)); + $all = $this->getAll($query); + $ret = array(); + $pk_name = $this->primaryKeyName($table_id); + if (is_array($all)) foreach ($all as $row) { + if (is_array($pk_name)) { + $pk = array(); + foreach ($pk_name as $pkn) $pk[$pkn] = $row[$pkn]; + } else { + $pk = $row[$pk_name]; + } + $ret[] = array( + 'table_id' => $table_id, + 'record_id' => $pk, + 'name' => $row['name'], + 'context' => $this->context($find, implode(' ', array_values($row))) + ); + } + return $ret; + } + + public function extractPrefix($str, $prefixes) + { + if (is_null($prefixes)) return ''; + if (!is_array($prefixes)) $prefixes = array($prefixes); + usort($prefixes, function ($a, $b) { + return strlen($b) - strlen($a); + }); + foreach ($prefixes as $prefix) { + if ($this->checkPrefix($prefix, $str)) return $prefix; + } + return ''; + } + + public function checkPrefix($prefix, $str, $case_insensitive = false) + { + if (is_array($prefix)) { + foreach ($prefix as $one) { + if ($this->checkPrefix($one, $str, $case_insensitive)) return true; + } + return false; + } + if ($case_insensitive) { + $prefix = strtolower($prefix); + $str = strtolower($str); + } + return $prefix == substr($str, 0, strlen($prefix)); + } + + public function allowedChars($str, $allowed = 'abcdefghijklmnopqrstuvwxyz0123456789') + { + $ret = ''; + for ($i = 0; $i < strlen($str); $i++) { + $char = substr($str, $i, 1); + if (strpos($allowed, $char) !== false) { + $ret .= $char; + } + } + return $ret; + } + + public function context($findme, $string, $around_words = 2) + { + $findme = strtolower($findme); + $parts = explode(' ', $string); + $is = array(); + for ($i = 0; $i < count($parts); $i++) { + if (strtolower($parts[$i]) == $findme) { + for ($j = max(0, $i - $around_words); $j < min(count($parts) - 1, $i + $around_words); $j++) { + if (!in_array($j, $is)) $is[] = $j; + } + } + } + $out = ''; + $last_i = 0; + foreach ($is as $i) { + if ($last_i + 1 < $i) $out .= ' ...'; + $out .= ' ' . $parts[$i]; + $last_i = $i; + } + return $out . ' ...'; + } + + public function arrayKeysToLowerCase(array $array, int $case = CASE_LOWER): array + { + $newArray = []; + foreach ($array as $key => $value) { + $newKey = is_string($key) + ? ($case === CASE_LOWER ? strtolower($key) : strtoupper($key)) + : $key; + if (is_array($value)) { + $value = $this->arrayKeysToLowerCase($value, $case); + } + $newArray[$newKey] = $value; + } + return $newArray; + } + +} + +class QueryBuilder +{ + private DBmodel $model; + + private $action = null; + private $tab_hash = 'tab'; + private $tab_key = null; + private $table_id = null; + private $tables = array(); + private $columns = array(); + private $vk_keys = array(); + private $vk_values = array(); + private $duplicates = array(); + private $sets = array(); + private $conditions = array(); + private $group_by = array(); + private $order_by = array(); + private $limit = null; + private $alias = null; + private $offset = null; + + public function __construct($_model) + { + $this->model = $_model; + } + + private function incTabKey() + { + $this->tab_key = $this->tab_hash . (count($this->tables) + 1); + } + + private function allAttributes($table_id) + { + $primary_key = $this->model->primaryKeyName($table_id); + $primary_key = (is_array($primary_key)) ? $primary_key : array($primary_key); + return array_merge($this->model->allowAttributes($table_id), $primary_key); + } + + public function beginAction($action, $table_id, $tab_hash = 'tab') + { + $this->action = $action; + $this->table_id = $table_id; + $this->tab_hash = $tab_hash; + if ( + $action == 'SELECT' + || $action == 'UPDATE' + ) { + $this->incTabKey(); + $this->tables[$this->tab_key] = sprintf('%s AS %s', $this->model->tableName($table_id), $this->tab_key); + } + if ( + $action == 'DELETE' + || $action == 'INSERT' + ) { + $this->tables[$this->tab_key] = sprintf('%s', $this->model->tableName($table_id)); + } + return $this; + } + + private function _join($join_type, $table_id, $left_attr, $right_attr, $joined_tab_key = null) + { + $this->table_id = $table_id; + $last_tab_key = is_null($joined_tab_key) ? $this->tab_key : $joined_tab_key; + $this->incTabKey(); + $left_attr = is_array($left_attr) ? $left_attr : array($left_attr); + $right_attr = is_array($right_attr) ? $right_attr : array($right_attr); + $min_count = min(count($left_attr), count($right_attr)); + if ($min_count <= 0) { + return $this; + } + $on = array(); + for ($index = 0; $index < $min_count; $index++) { + $on[] = sprintf( + '%s%s = %s%s', + (!in_array(substr($left_attr[$index], 0, 1), array('"', ' '))) ? $last_tab_key . '.' : '', + $left_attr[$index], + (!in_array(substr($right_attr[$index], 0, 1), array('"', ' '))) ? $this->tab_key . '.' : '', + $right_attr[$index] + ); + } + $this->tables[$this->tab_key] = sprintf( + '%s %s AS %s ON %s', + $join_type, + $this->model->tableName($table_id), + $this->tab_key, + implode(' AND ', $on) + ); + return $this; + } + + public function join($table_id, $left_attr, $right_attr, $joined_tab_key = null) + { + return $this->_join('JOIN', $table_id, $left_attr, $right_attr, $joined_tab_key); + } + + public function leftjoin($table_id, $left_attr, $right_attr, $joined_tab_key = null) + { + return $this->_join('LEFT JOIN', $table_id, $left_attr, $right_attr, $joined_tab_key); + } + + public function rightjoin($table_id, $left_attr, $right_attr, $joined_tab_key = null) + { + return $this->_join('RIGHT JOIN', $table_id, $left_attr, $right_attr, $joined_tab_key); + } + + public function column($colname, $as = null) + { + if (is_array($colname)) { + foreach ($colname as $col) { + $this->column($col, $as); + } + } else { + $allow_attributes = $this->allAttributes($this->table_id); + if (in_array($colname, $allow_attributes)) { + $column = sprintf('%s.`%s`', $this->tab_key, $colname); + } else if ($colname == '*') { + $column = sprintf('%s.*', $this->tab_key); + } else if ( + is_object($colname) + && get_class($colname) == 'QueryBuilder' + ) { + $column = sprintf('(%s) AS %s', $colname->completeQuery(), $colname->alias); + } else { + $column = sprintf('%s', $colname); + } + if (!is_null($as)) { + $column .= ' AS ' . $as; + } + $this->columns[] = $column; + } + return $this; + } + + public function set($set = array()) + { + $allow_attributes = $this->model->allowAttributes($this->table_id); + $set = $this->model->fixDecimalPoints($this->table_id, $set); + $set = $this->model->fixBooleans($this->table_id, $set); + $set = $this->model->fixJSONs($this->table_id, $set); + $set = $this->model->fixTypes($this->table_id, $set); + if (is_array($set)) foreach ($set as $key => $val) { + if (in_array($key, $allow_attributes)) { + if (is_null($val)) { + $this->sets[] = sprintf('%s.%s = NULL', $this->tab_key, $key); + } else if ( + is_object($val) + && get_class($val) == 'QueryBuilder' + ) { + $this->sets[] = sprintf('%s.%s = (%s)', $this->tab_key, $key, $val->completeQuery()); + } else { + $q_val = (substr($val, 0, 1) == '_' && substr($val, -1) == '_') ? substr($val, 1, -1) : $this->model->quote($val); + $this->sets[] = sprintf('%s.%s = %s', $this->tab_key, $key, $q_val); + } + } + } + return $this; + } + + public function value($values = array()) + { + $allow_attributes = $this->model->allowAttributes($this->table_id); + if (is_array($values)) foreach ($values as $key => $val) { + if (in_array($key, $allow_attributes)) { + $this->vk_keys[] = sprintf('`%s`', $key); + $this->vk_values[] = is_null($val) ? 'NULL' : sprintf('%s', $this->model->quote($val)); + } + } + return $this; + } + + public function duplicate($dupls = array()) + { + $allow_attributes = $this->model->allowAttributes($this->table_id); + $dupls = $this->model->fixTypes($this->table_id, $dupls); + if (is_array($dupls)) foreach ($dupls as $key => $val) { + if (in_array($key, $allow_attributes)) { + if (is_null($val)) { + $this->duplicates[] = sprintf('%s = NULL', $key); + } else { + $this->duplicates[] = sprintf('%s = %s', $key, $this->model->quote($val)); + } + } + } + return $this; + } + + private $where_prefixes = array('!', '%', '<', '<=', '>', '>='); + public function where($search = array(), $concat_or = false) + { + $allow_attributes = $this->allAttributes($this->table_id); + $search = $this->model->fixTypes($this->table_id, $search, $this->where_prefixes); + $_tab_key = (strlen($this->tab_key) > 0) ? $this->tab_key . '.' : ''; + $conds = array(); + if (is_array($search)) foreach ($search as $key => $val) { + if (is_string($key)) { + $as_no_equal = (substr($key, 0, 1) == '!'); + } + if (is_string($val)) { + if (strlen($val) <= 0) continue; + $as_like = (stristr($val, '%') !== false); + $as_lesser_equal = (substr($val, 0, 2) == '<='); + $as_lesser = (!$as_lesser_equal && substr($val, 0, 1) == '<'); + $as_greater_equal = (substr($val, 0, 2) == '>='); + $as_greater = (!$as_greater_equal && substr($val, 0, 1) == '>'); + $as_no_equal = $as_no_equal || (substr($val, 0, 1) == '!'); + } + if ( + is_array($val) + && count($val) <= 0 + ) { + continue; + } + $key = trim(str_replace(array('%', '!'), '', $key)); + if ( + in_array($key, $allow_attributes) + || (substr($key, 0, 1) == '_' && substr($key, -1) == '_') + || (substr($key, 0, 1) == '*' && substr($key, -1) == '*') + ) { + $__tab_key = $_tab_key; + if ( + substr($key, 0, 1) == '_' + && substr($key, -1) == '_' + ) { + $key = $this->model->quote(substr($key, 1, -1)); + $__tab_key = ''; + } + if ( + substr($key, 0, 1) == '*' + && substr($key, -1) == '*' + ) { + $key = substr($key, 1, -1); + $__tab_key = ''; + } + if (is_null($val)) { + $conds[] = sprintf('%s%s IS NULL', $__tab_key, $key); + } else if ( + is_object($val) + && get_class($val) == 'QueryBuilder' + ) { + $conds[] = sprintf('%s%s %s IN (%s)', $__tab_key, $key, $as_no_equal ? 'NOT' : '', $val->completeQuery()); + } else if (is_array($val)) { + $values = array(); + foreach ($val as $v) { + if (is_null($v)) continue; + $values[] = $this->model->quote($v); + } + $add_or = (in_array(null, $val, true)) ? sprintf('OR %s%s IS NULL', $__tab_key, $key) : ''; + $conds[] = sprintf('(%s%s %s IN (%s) %s)', $__tab_key, $key, $as_no_equal ? 'NOT' : '', implode(', ', $values), $add_or); + } else if (in_array(strtolower($val), array('not null', 'is not null', '! null', '!null', '!= null', '!=null'))) { + $conds[] = sprintf('%s%s IS NOT NULL', $__tab_key, $key); + } else { + if ($as_like) { + $sign = 'LIKE'; + } else if ($as_lesser) { + $sign = '<'; + $val = substr($val, 1); + } else if ($as_lesser_equal) { + $sign = '<='; + $val = substr($val, 2); + } else if ($as_greater) { + $sign = '>'; + $val = substr($val, 1); + } else if ($as_greater_equal) { + $sign = '>='; + $val = substr($val, 2); + } else if ($as_no_equal) { + $sign = '!='; + $val = substr($val, 1); + } else { + $sign = '='; + } + $q_val = (substr($val, 0, 1) == '`' && substr($val, -1) == '`') ? $val : $this->model->quote($val); + $q_val = (substr($val, 0, 1) == '_' && substr($val, -1) == '_') ? substr($val, 1, -1) : $q_val; + $conds[] = sprintf('%s%s %s %s', $__tab_key, $key, $sign, $q_val); + } + } + } + if ($concat_or) { + if (count($conds) > 0) { + $this->conditions[] = '(' . implode(' OR ', $conds) . ')'; + } + } else { + $this->conditions = array_merge($this->conditions, $conds); + } + return $this; + } + + public function whereOR($search = array()) + { + return $this->where($search, true); + } + + public function group($group) + { + if (is_array($group)) { + foreach ($group as $g) { + $this->group($g); + } + } else { + $allow_attributes = $this->allAttributes($this->table_id); + if (in_array($group, $allow_attributes)) { + $this->group_by[] = sprintf('%s.%s', $this->tab_key, $group); + } else if (substr($group, 0, 1) == '`' && substr($group, -1) == '`') { + $this->group_by[] = sprintf('%s', $group); + } + } + return $this; + } + + public function order($order = array()) + { + $allow_attributes = $this->allAttributes($this->table_id); + if (is_array($order)) foreach ($order as $key => $direction) { + $direction_sign = in_array(strtolower($direction), array('desc', 'descending', 'down')) ? 'DESC' : 'ASC'; + if (in_array($key, $allow_attributes)) { + $this->order_by[] = sprintf('%s.%s %s', $this->tab_key, $key, $direction_sign); + } else if (substr($key, 0, 1) == '`' && substr($key, -1) == '`') { + $this->order_by[] = sprintf('%s %s', $key, $direction_sign); + } + } + return $this; + } + + public function limit($count_or_from, $count = null) + { + if (is_null($count_or_from)) return $this; + if (is_array($count_or_from)) { + $count = $count_or_from[1]; + $count_or_from = $count_or_from[0]; + } + if (is_null($count)) { + $this->limit = sprintf('LIMIT %d', intval($count_or_from)); + } else { + $this->limit = sprintf('LIMIT %d,%d', intval($count_or_from), intval($count)); + } + return $this; + } + + public function offset($offset) + { + if (is_null($offset)) return $this; + $this->offset = sprintf('OFFSET %d', intval($offset)); + return $this; + } + + private function completeQuery() + { + if ($this->action == 'SELECT') { + $query = sprintf( + 'SELECT %s' + . ' FROM %s' + . ' %s' // WHERE + . ' %s' // GROUP + . ' %s' // ORDER + . ' %s' // LIMIT + . ' %s', // OFFSET + (count($this->columns) > 0) ? implode(', ', $this->columns) : '*', + implode(' ', $this->tables), + (count($this->conditions) > 0) ? 'WHERE ' . implode(' AND ', $this->conditions) : '', + (count($this->group_by) > 0) ? 'GROUP BY ' . implode(', ', $this->group_by) : '', + (count($this->order_by) > 0) ? 'ORDER BY ' . implode(', ', $this->order_by) : '', + is_null($this->limit) ? '' : $this->limit, + is_null($this->offset) ? '' : $this->offset + ); + } else if ($this->action == 'INSERT') { + $query = sprintf( + 'INSERT INTO %s' + . ' (%s)' // KEYS + . ' VALUES' + . ' (%s)' // VALUES + . ' %s', // ON DUPLICATE + implode(' ', $this->tables), + implode(', ', $this->vk_keys), + implode(', ', $this->vk_values), + ((count($this->duplicates) > 0) ? 'ON DUPLICATE KEY UPDATE ' . implode(', ', $this->duplicates) : '') + ); + } else if ($this->action == 'UPDATE') { + $query = sprintf( + 'UPDATE %s' + . ' %s' // SET + . ' %s' // WHERE + . ' %s', // LIMIT + implode(' ', $this->tables), + (count($this->sets) > 0) ? 'SET ' . implode(', ', $this->sets) : '', + (count($this->conditions) > 0) ? 'WHERE ' . implode(' AND ', $this->conditions) : '', + is_null($this->limit) ? '' : $this->limit + ); + } else if ($this->action == 'DELETE') { + $query = sprintf( + 'DELETE FROM %s' + . ' %s' // WHERE + . ' %s', // LIMIT + implode(' ', $this->tables), + (count($this->conditions) > 0) ? 'WHERE ' . implode(' AND ', $this->conditions) : '', + is_null($this->limit) ? '' : $this->limit + ); + } else { + throw new \Exception('Unknown action "' . $this->action . '"'); + } + $this->model->_debug('[Model][completeQuery]: ' . $query); + return $query; + } + + public function alias($alias) + { + $this->alias = $alias; + return $this; + } + + public function toArray() + { + $query = $this->completeQuery(); + $all = $this->model->getAll($query); + if (is_array($all)) foreach ($all as $index => $row) { + $all[$index] = $this->model->unfixJSONs($this->table_id, $row); + } + return $all; + } + + public function getCol() + { + $query = $this->completeQuery(); + return $this->model->getCol($query); + } + + public function getOne() + { + $query = $this->completeQuery(); + return $this->model->getOne($query); + } + + public function toArrayFirst() + { + $query = $this->completeQuery(); + $row = $this->model->getRow($query); + $row = $this->model->unfixJSONs($this->table_id, $row); + return $row; + } + + public function toCombo($name_key, $name_value, $add_empty = false) + { + $all = $this->toArray(); + $ret = array(); + if ($add_empty) $ret[''] = ''; + if (is_array($all)) foreach ($all as $row) { + $ret[$row[$name_key]] = $row[$name_value]; + } + return $ret; + } + + public function getRow() + { + return $this->toArrayFirst(); + } + + public function execute() + { + $query = $this->completeQuery(); + if (!$this->model->query($query)) return false; + return $this->model->affectedRows(); + } +} diff --git a/src/creator.php b/src/creator.php new file mode 100644 index 0000000..92d8e1c --- /dev/null +++ b/src/creator.php @@ -0,0 +1,176 @@ +dbh = $dbh; + } + + public function interact() { + $name = $this->readline('New model name: '); + $table = $this->readline('Table name: '); + $entity = $this->readline('Entity name: '); + if (strlen($entity) <= 0) { + $entity = $table; + } + $model_filepath = $this->rootDir().'/models/'.$name.'.php'; + if ($this->readline("Create MODEL class '$model_filepath'? (y - yes, other - no) ") != 'y') { + echo "Creating of MODEL class is skipped\n"; + return false; + } + $model_dirpath = dirname($model_filepath); + if (!file_exists($model_dirpath)) { + if ($this->readline("Create directory '$model_dirpath'? (y - yes, other - no) ") != 'y') { + echo "Creating of MODEL class is skipped\n"; + return false; + } + mkdir($model_dirpath); + } + echo "Creating MODEL class ... "; + if (file_exists($model_filepath)) { + if ($this->readline("File '$model_filepath' exists. Do you want to replace? (y - yes, other - no) ") != 'y') { + echo "Creating of MODEL class is skipped\n"; + return false; + } + } + $table_columns = $this->dbh->getTableColumns($table); + $suc = $this->modelSave($model_filepath, $name, $table, $entity, $table_columns['pks'], $table_columns['columns'], $table_columns['types']); + echo $suc ? "MODEL class created" : 'Error: MODEL class not created'; + } + + private function modelSave($filepath, $name, $tablename, $entity, $pks, $columns, $types) { + if (is_array($pks) && count($pks) > 1) { + $primary_key_name = "array('".implode("', '", $pks)."')"; + $columns = array_merge($pks, $columns); + } else { + if (is_array($pks)) { + $primary_key_name = "'".$pks[0]."'"; + } else { + $primary_key_name = "'$pks'"; + } + } + $allowed_attributes = array(); + foreach ($columns as $column) { + $allowed_attributes[] = sprintf("'%s' => '%s'", $column, str_replace("'", '"', $types[$column])); + } + if (substr($entity, -3) == 'ies') { + $entity_one = substr($entity, 0, -3).'y'; + } else if (substr($entity, -2) == 'es') { + $entity_one = substr($entity, 0, -2); + } else if (substr($entity, -1) == 's') { + $entity_one = substr($entity, 0, -1); + } else { + $entity_one = $entity; + } + $auto_created_dt = in_array('created_dt', $columns) + ? ' + if (is_null($primary_key) + && !isset($data[\'created_dt\'])) + { + $data[\'created_dt\'] = date(\'Y-m-d H:i:s\'); + }' + : ''; + $auto_changed_dt = in_array('changed_dt', $columns) + ? ' + if (!is_null($primary_key) + && is_array($data) + && count($data) > 0 + && !isset($data[\'changed_dt\'])) + { + $data[\'changed_dt\'] = date(\'Y-m-d H:i:s\'); + }' + : ''; + $content = ' array( + 'name' => '".$entity."', + 'primary_key_name' => ".$primary_key_name.", + 'allow_attributes' => array( + ".implode(",\n\t\t\t\t", $allowed_attributes)." + ) + ), + ); + + public function exist($"."primary_key = null) { + return $"."this->existRecord('".$entity."', $"."primary_key); + } + + public function ".$entity_one."($"."primary_key = null, $"."data = array()) {".$auto_created_dt.$auto_changed_dt." + return $"."this->record('".$entity."', $"."primary_key, $"."data); + } + + public function ".$entity_one."By($"."colname, $"."colvalue) { + return $"."this->recordBy('".$entity."', $"."colname, $"."colvalue); + } + + public function ".$entity_one."Save($"."data = array()) { + return $"."this->".$entity_one."($"."this->exist($"."data) ? $"."data : null, $"."data); + } + + public function ".$entity_one."Empty() { + return $"."this->recordEmpty('".$entity."'); + } + + public function ".$entity_one."Attributes() { + return $"."this->typesAttributes('".$entity."'); + } + + public function ".$entity_one."Count() { + return $"."this->count('".$entity."'); + } + + public function getList($"."search = array(), $"."reverse = false, $"."concat_or = false) { + return $"."this->search('".$entity."') + ->where($"."search, $"."concat_or) + ->order(array(".$primary_key_name." => $"."reverse ? 'DESC' : 'ASC')) + ->toArray(); + } + + public function ".$entity_one."Combo($"."col_key, $"."col_value, $"."add_empty = false) { + return $"."this->search('".$entity."') + ->toCombo($"."col_key, $"."col_value, $"."add_empty); + } + +} + +?"."> +"; + return file_put_contents($filepath, $content); + } + + /* ---------------------------------------------------- + * HELPDER METHODS + */ + public function readline($question, $default = null) { + ob_flush(); + $line = readline($question); + if (!is_null($default) + && strlen($line) <= 0) + { + return $default; + } + return $line; + } + + public function rootDir() { + return dirname(dirname(__FILE__)); + } + +} + + +?> diff --git a/test/test1.php b/test/test1.php new file mode 100644 index 0000000..034d9a3 --- /dev/null +++ b/test/test1.php @@ -0,0 +1,28 @@ +query("show tables"); +print_r($result); + +// test results +$result = $db->getAll("show databases"); +print_r($result); + +$result = $db->getAll("show tables"); +print_r($result); + +$result = $db->getCol("show tables"); +print_r($result); + +$result = $db->getTableColumns("test"); +print_r($result); + +$result = $db->getRow("select * from test where id = 10"); +print_r($result); + + +?> \ No newline at end of file diff --git a/test/test2.php b/test/test2.php new file mode 100644 index 0000000..69c3e6e --- /dev/null +++ b/test/test2.php @@ -0,0 +1,12 @@ +interact(); + + +?> \ No newline at end of file diff --git a/test/test3.php b/test/test3.php new file mode 100644 index 0000000..1083033 --- /dev/null +++ b/test/test3.php @@ -0,0 +1,38 @@ +exist(11); +print_r($result); + +// insert new record +$result = $test->test(null, array('name' => 'new record')); +print_r($result); + +// update record ID 11 +$result = $test->test(11, array('name' => 'updated record')); +print_r($result); + +// get record ID 11 +$result = $test->test(11); +print_r($result); + +// delete record ID 11 +$result = $test->test(11, null); +print_r($result); + +// get all records +$result = $test->getList(); +print_r($result); + +// get combined list of ID and name +$result = $test->testCombo('id', 'name'); +print_r($result); + +?> \ No newline at end of file