From 13f3a5a1820b5e63d2d6680097b4f17ab9965dec Mon Sep 17 00:00:00 2001 From: nguyenanhung Date: Fri, 13 Sep 2024 22:10:13 +0700 Subject: [PATCH] Fix disconnect database --- ...CI_Base_Custom_Model_Credentials_model.php | 5 +- hungng/HungNG_Custom_Based_model.php | 2798 ++++++------ hungng/HungNG_Model.php | 3975 +++++++++-------- hungng/HungNG_ORM_Model.php | 3603 +++++++-------- 4 files changed, 5207 insertions(+), 5174 deletions(-) diff --git a/custom/HungNG_CI_Base_Custom_Model_Credentials_model.php b/custom/HungNG_CI_Base_Custom_Model_Credentials_model.php index 17e707b..5d0fd81 100644 --- a/custom/HungNG_CI_Base_Custom_Model_Credentials_model.php +++ b/custom/HungNG_CI_Base_Custom_Model_Credentials_model.php @@ -53,7 +53,10 @@ public function __destruct() { if ($this->db->conn_id) { $this->db->close(); - log_message('info', 'HungNG_CI_Base_Custom_Model_Credentials_model Class Destructed'); + log_message( + 'info', + 'Model "HungNG_CI_Base_Custom_Model_Credentials_model" Class Destructed and database disconnected' + ); } } diff --git a/hungng/HungNG_Custom_Based_model.php b/hungng/HungNG_Custom_Based_model.php index 07fec55..404f2e3 100644 --- a/hungng/HungNG_Custom_Based_model.php +++ b/hungng/HungNG_Custom_Based_model.php @@ -10,1406 +10,1414 @@ * Time: 13:50 */ if (!class_exists('HungNG_Custom_Based_model')) { - /** - * Class HungNA_Based_model - * - * @author 713uk13m - * @copyright 713uk13m - * - * @property CI_Benchmark $benchmark This class enables you to mark points and calculate the time difference between them. Memory consumption can also be displayed. - * @property CI_Calendar $calendar This class enables the creation of calendars - * @property CI_Cache $cache Caching Class - * @property CI_Cart $cart Shopping Cart Class - * @property CI_Config $config This class contains functions that enable config files to be managed - * @property CI_Controller $controller This class object is the super class that every library in CodeIgniter will be assigned to - * @property CI_DB_forge $dbforge Database Forge Class - * @property CI_DB_pdo_driver|CI_DB_query_builder|CI_DB_driver $db This is the platform-independent base Query Builder implementation class - * @property CI_DB_utility $dbutil Database Utility Class - * @property CI_Driver_Library $driver Driver Library Class - * @property CI_Email $email Permits email to be sent using Mail, Sendmail, or SMTP - * @property CI_Encrypt $encrypt Provides two-way keyed encoding using Mcrypt - * @property CI_Encryption $encryption Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions - * @property CI_Exceptions $exceptions Exceptions Class - * @property CI_Form_validation $form_validation Form Validation Class - * @property CI_FTP $ftp FTP Class - * @property CI_Hooks $hooks Provides a mechanism to extend the base system without hacking - * @property CI_Image_lib $image_lib Image Manipulation class - * @property CI_Input $input Pre-processes global input data for security - * @property CI_Javascript $javascript Javascript Class - * @property CI_Jquery $jquery Jquery Class - * @property CI_Lang $lang Language Class - * @property CI_Loader $load Loads framework components - * @property CI_Log $log Logging Class - * @property CI_Migration $migration All migrations should implement this, forces up() and down() and gives access to the CI super-global - * @property CI_Model $model CodeIgniter Model Class - * @property CI_Output $output Responsible for sending final output to the browser - * @property CI_Pagination $pagination Pagination Class - * @property CI_Parser $parser Parser Class - * @property CI_Profiler $profiler This class enables you to display benchmark, query, and other data in order to help with debugging and optimization. - * @property CI_Router $router Parses URIs and determines routing - * @property CI_Security $security Security Class - * @property CI_Session $session Session Class - * @property CI_Table $table Lets you create tables manually or from database result objects, or arrays - * @property CI_Trackback $trackback Trackback Sending/Receiving Class - * @property CI_Typography $typography Typography Class - * @property CI_Unit_test $unit Simple testing class - * @property CI_Upload $upload File Uploading Class - * @property CI_URI $uri Parses URIs and determines routing - * @property CI_User_agent $agent Identifies the platform, browser, robot, or mobile device of the browsing agent - * @property CI_Xmlrpc $xmlrpc XML-RPC request handler class - * @property CI_Xmlrpcs $xmlrpcs XML-RPC server class - * @property CI_Zip $zip Zip Compression Class - * @property CI_Utf8 $utf8 Provides support for UTF-8 environments - */ - class HungNG_Custom_Based_model extends CI_Model - { - const OPERATOR_EQUAL_TO = '='; - const OPERATOR_NOT_EQUAL_TO = '!='; - const OPERATOR_LESS_THAN = '<'; - const OPERATOR_LESS_THAN_OR_EQUAL_TO = '<='; - const OPERATOR_GREATER_THAN = '>'; - const OPERATOR_GREATER_THAN_OR_EQUAL_TO = '>='; - const OPERATOR_IS_SPACESHIP = '<=>'; - const OPERATOR_IS_IN = 'IN'; - const OPERATOR_IS_LIKE = 'LIKE'; - const OPERATOR_IS_LIKE_BINARY = 'LIKE BINARY'; - const OPERATOR_IS_ILIKE = 'ilike'; - const OPERATOR_IS_NOT_LIKE = 'NOT LIKE'; - const OPERATOR_IS_NULL = 'IS NULL'; - const OPERATOR_IS_NOT_NULL = 'IS NOT NULL'; - const ORDER_ASCENDING = 'ASC'; - const ORDER_DESCENDING = 'DESC'; - const ORDER_RANDOM = 'RAND'; - const DEFAULT_STATUS_IS_ACTIVE = 1; - const DEFAULT_STATUS_IS_DE_ACTIVE = 0; - - /** @var \CI_DB_query_builder $db */ - protected $db; - - /** @var string $tableName */ - protected $tableName; - - /** @var string $primary_key */ - protected $primary_key; - - /** @var string $is_not $is_not */ - protected $is_not; - - /** @var string $or_higher */ - protected $or_higher; - - /** @var string $is_higher */ - protected $is_higher; - - /** @var string $or_smaller */ - protected $or_smaller; - - /** @var string $is_smaller */ - protected $is_smaller; - - /** @var array $field */ - protected $field = array(); - - /** @var string $start_time */ - protected $start_time; - - /** @var string $end_time */ - protected $end_time; - - /** @var string $created_at */ - protected $created_at; - - /** @var string $updated_at */ - protected $updated_at; - - /** @var string $deleted_at */ - protected $deleted_at; - - /** @var string $published_at */ - protected $published_at; - - /** - * HungNG_Custom_Based_model constructor. - * - * @author : 713uk13m - * @copyright: 713uk13m - */ - public function __construct() - { - parent::__construct(); - - log_message('info', 'HungNG_Custom_Based_model Class Initialized'); - - $this->db = $this->load->database('default', true, true); - $this->tableName = ''; - $this->primary_key = 'id'; - $this->created_at = 'created_at'; - $this->updated_at = 'updated_at'; - $this->deleted_at = 'deleted_at'; - $this->published_at = 'published_at'; - $this->is_not = ' !='; - $this->or_higher = ' >='; - $this->is_higher = ' >'; - $this->or_smaller = ' <='; - $this->is_smaller = ' <'; - $this->start_time = ' 00:00:00'; - $this->end_time = ' 23:59:59'; - } - - /** - * __destruct models - */ + /** + * Class HungNA_Based_model + * + * @author 713uk13m + * @copyright 713uk13m + * + * @property CI_Benchmark $benchmark This class enables you to mark points and calculate the time difference between them. Memory consumption can also be displayed. + * @property CI_Calendar $calendar This class enables the creation of calendars + * @property CI_Cache $cache Caching Class + * @property CI_Cart $cart Shopping Cart Class + * @property CI_Config $config This class contains functions that enable config files to be managed + * @property CI_Controller $controller This class object is the super class that every library in CodeIgniter will be assigned to + * @property CI_DB_forge $dbforge Database Forge Class + * @property CI_DB_pdo_driver|CI_DB_query_builder|CI_DB_driver $db This is the platform-independent base Query Builder implementation class + * @property CI_DB_utility $dbutil Database Utility Class + * @property CI_Driver_Library $driver Driver Library Class + * @property CI_Email $email Permits email to be sent using Mail, Sendmail, or SMTP + * @property CI_Encrypt $encrypt Provides two-way keyed encoding using Mcrypt + * @property CI_Encryption $encryption Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions + * @property CI_Exceptions $exceptions Exceptions Class + * @property CI_Form_validation $form_validation Form Validation Class + * @property CI_FTP $ftp FTP Class + * @property CI_Hooks $hooks Provides a mechanism to extend the base system without hacking + * @property CI_Image_lib $image_lib Image Manipulation class + * @property CI_Input $input Pre-processes global input data for security + * @property CI_Javascript $javascript Javascript Class + * @property CI_Jquery $jquery Jquery Class + * @property CI_Lang $lang Language Class + * @property CI_Loader $load Loads framework components + * @property CI_Log $log Logging Class + * @property CI_Migration $migration All migrations should implement this, forces up() and down() and gives access to the CI super-global + * @property CI_Model $model CodeIgniter Model Class + * @property CI_Output $output Responsible for sending final output to the browser + * @property CI_Pagination $pagination Pagination Class + * @property CI_Parser $parser Parser Class + * @property CI_Profiler $profiler This class enables you to display benchmark, query, and other data in order to help with debugging and optimization. + * @property CI_Router $router Parses URIs and determines routing + * @property CI_Security $security Security Class + * @property CI_Session $session Session Class + * @property CI_Table $table Lets you create tables manually or from database result objects, or arrays + * @property CI_Trackback $trackback Trackback Sending/Receiving Class + * @property CI_Typography $typography Typography Class + * @property CI_Unit_test $unit Simple testing class + * @property CI_Upload $upload File Uploading Class + * @property CI_URI $uri Parses URIs and determines routing + * @property CI_User_agent $agent Identifies the platform, browser, robot, or mobile device of the browsing agent + * @property CI_Xmlrpc $xmlrpc XML-RPC request handler class + * @property CI_Xmlrpcs $xmlrpcs XML-RPC server class + * @property CI_Zip $zip Zip Compression Class + * @property CI_Utf8 $utf8 Provides support for UTF-8 environments + */ + class HungNG_Custom_Based_model extends CI_Model + { + const OPERATOR_EQUAL_TO = '='; + const OPERATOR_NOT_EQUAL_TO = '!='; + const OPERATOR_LESS_THAN = '<'; + const OPERATOR_LESS_THAN_OR_EQUAL_TO = '<='; + const OPERATOR_GREATER_THAN = '>'; + const OPERATOR_GREATER_THAN_OR_EQUAL_TO = '>='; + const OPERATOR_IS_SPACESHIP = '<=>'; + const OPERATOR_IS_IN = 'IN'; + const OPERATOR_IS_LIKE = 'LIKE'; + const OPERATOR_IS_LIKE_BINARY = 'LIKE BINARY'; + const OPERATOR_IS_ILIKE = 'ilike'; + const OPERATOR_IS_NOT_LIKE = 'NOT LIKE'; + const OPERATOR_IS_NULL = 'IS NULL'; + const OPERATOR_IS_NOT_NULL = 'IS NOT NULL'; + const ORDER_ASCENDING = 'ASC'; + const ORDER_DESCENDING = 'DESC'; + const ORDER_RANDOM = 'RAND'; + const DEFAULT_STATUS_IS_ACTIVE = 1; + const DEFAULT_STATUS_IS_DE_ACTIVE = 0; + + /** @var \CI_DB_query_builder $db */ + protected $db; + + /** @var string $tableName */ + protected $tableName; + + /** @var string $primary_key */ + protected $primary_key; + + /** @var string $is_not $is_not */ + protected $is_not; + + /** @var string $or_higher */ + protected $or_higher; + + /** @var string $is_higher */ + protected $is_higher; + + /** @var string $or_smaller */ + protected $or_smaller; + + /** @var string $is_smaller */ + protected $is_smaller; + + /** @var array $field */ + protected $field = array(); + + /** @var string $start_time */ + protected $start_time; + + /** @var string $end_time */ + protected $end_time; + + /** @var string $created_at */ + protected $created_at; + + /** @var string $updated_at */ + protected $updated_at; + + /** @var string $deleted_at */ + protected $deleted_at; + + /** @var string $published_at */ + protected $published_at; + + /** + * HungNG_Custom_Based_model constructor. + * + * @author : 713uk13m + * @copyright: 713uk13m + */ + public function __construct() + { + parent::__construct(); + + log_message('info', 'HungNG_Custom_Based_model Class Initialized'); + + $this->db = $this->load->database('default', true, true); + $this->tableName = ''; + $this->primary_key = 'id'; + $this->created_at = 'created_at'; + $this->updated_at = 'updated_at'; + $this->deleted_at = 'deleted_at'; + $this->published_at = 'published_at'; + $this->is_not = ' !='; + $this->or_higher = ' >='; + $this->is_higher = ' >'; + $this->or_smaller = ' <='; + $this->is_smaller = ' <'; + $this->start_time = ' 00:00:00'; + $this->end_time = ' 23:59:59'; + } + + /** + * __destruct models + */ public function __destruct() { if ($this->db->conn_id) { $this->db->close(); + log_message( + 'info', + 'Class "HungNG_Custom_Based_model" Class Destructed and database disconnected' + ); + } + } + + /** + * Function setDb + * + * @param string $db_group + * + * @return $this + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 12/09/2020 38:49 + */ + public function setDb($db_group = '') + { + $this->db = $this->load->database($db_group, true, true); + + return $this; + } + + /** + * Function setTableName + * + * @param string $tableName + * + * @return $this + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 30:49 + */ + public function setTableName($tableName = '') + { + $this->tableName = $tableName; + + return $this; + } + + /** + * Function getTableName + * + * @return string + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 30:45 + */ + public function getTableName() + { + return $this->tableName; + } + + /** + * Function bindDBPrefix + * + * @param $table + * + * @return string + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 27/03/2023 53:42 + */ + public function bindDBPrefix($table) + { + if (!empty($this->db->dbprefix)) { + if (strpos($table, $this->db->dbprefix)) { + return $table; + } + } + + return $this->db->dbprefix($table); + } + + /** + * Function close + * + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/25/2021 52:39 + */ + public function close() + { + $this->db->close(); + log_message( + 'info', + 'Class "HungNG_Custom_Based_model" database closed' + ); + } + + // ---------------------------------------------------------------------------------------------------------------------------------------- // + + /** + * Function get_off_set + * + * @param $size + * @param $page + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 22/03/2023 13:24 + */ + public function get_off_set($size = 500, $page = 0) + { + $size = (int)$size; + $page = (int)$page; + if ($page !== 0) { + if ($page <= 0 || empty($page)) { + $page = 1; + } + $start = ($page - 1) * $size; + } else { + $start = $page; + } + + return (int)$start; + } + + /** + * Function page_limit + * + * @param int $size + * @param int $page + * + * @return \CI_DB_query_builder + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 03/06/2022 20:38 + */ + public function page_limit($size = 500, $page = 0) + { + if ($size !== 'no_limit') { + $start = $this->get_off_set($size, $page); + + return $this->db->limit($size, $start); + } + + return $this->db; + } + + /** + * Function _page_limit alias of page_limit + * + * @param int $size + * @param int $page + * + * @return \CI_DB_query_builder + * @deprecated use page_limit method + * @author : 713uk13m + * @copyright : 713uk13m + * @time : 08/16/2021 30:54 + */ + public function _page_limit($size = 500, $page = 0) + { + return $this->page_limit($size, $page); + } + + /** + * Function bind_order_result + * + * Lưu ý: mặc định sẽ order theo giá trị $direction và $field, $field truyền vào + * Trong trường hợp sử dụng $table=order_by_field, cần setup cả table và field trong biến $order_by_field trước khi truyền vào để order cho đúng + * + * @param $order_by_field + * @param $direction + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 11/02/2023 49:18 + */ + public function bind_order_result($order_by_field, $direction = 'desc', $field = 'id', $table = '') + { + return $this->build_order_result($order_by_field, $direction, $field, $table); + } + + /** + * Function build_order_result + * + * Lưu ý: mặc định sẽ order theo giá trị $direction và $field, $field truyền vào + * Trong trường hợp sử dụng $table=order_by_field, cần setup cả table và field trong biến $order_by_field trước khi truyền vào để order cho đúng + * + * @param $order_by_field + * @param $direction + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 28:15 + */ + public function build_order_result($order_by_field, $direction = 'desc', $field = 'id', $table = '') + { + if (!empty($table)) { + $tableName = $this->bindDBPrefix(trim($table)) . '.'; + } else { + $tableName = $this->bindDBPrefix($this->tableName) . '.'; + } + if ($table === 'order_by_field') { + $tableName = ''; + } + if (isset($order_by_field) && is_array($order_by_field) && count($order_by_field) > 0) { + foreach ($order_by_field as $f) { + $this->db->order_by($tableName . $f['field_name'], $f['order_value']); + } + } else { + $direction = strtoupper(trim($direction)); + if ($direction === 'RANDOM') { + $this->db->order_by($tableName . $field, 'RANDOM'); + } else { + $this->db->order_by($tableName . $field, $direction); + } + } + + return $this->db; + } + + /** + * Function build_list_id_with_parent_id - Tạo 1 list các ID, trong đó chứa các tập con phụ thuộc của ID đ + * + * @param array|object|mixed $allSubId + * @param string|int $parentId + * + * @return array|string|int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 13:31 + */ + public function build_list_id_with_parent_id($allSubId, $parentId) + { + if (is_array($allSubId) || is_object($allSubId)) { + // Xác định lấy toàn bộ tin tức ở các category con + $countSub = count($allSubId); // Đếm bảng ghi Category con + if ($countSub) { + // Nếu tồn tại các category con + $listSub = array(); + $listSub[] = $parentId; // Push category cha + foreach ($allSubId as $item) { + $itemId = is_array($item) ? $item['id'] : $item->id; + $listSub[] = $itemId; // Push các category con vào mảng dữ liệu + } + + return $listSub; + } + } + + return $parentId; + } + + /** + * Function prepare_simple_wheres_not_statement + * + * @param $value + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 31:03 + */ + public function prepare_simple_wheres_not_statement($value, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + if ($value !== null) { + if (is_array($value)) { + $this->db->where_not_in($tableName . '.' . $field, $value); + } else { + $this->db->where($tableName . '.' . $field . $this->is_not, $value); + } + } + + return $this->db; + } + + /** + * Function prepare_simple_wheres_statement + * + * @param $value + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 31:11 + */ + public function prepare_simple_wheres_statement($value, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + if ($value !== null) { + if (is_array($value)) { + $this->db->where_in($tableName . '.' . $field, $value); + } else { + $this->db->where($tableName . '.' . $field, $value); + } + } + + return $this->db; + } + + /** + * Function prepare_wheres_statement + * + * @param $wheres + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 50:08 + */ + public function prepare_wheres_statement($wheres) + { + if (!empty($wheres) && is_array($wheres) && count($wheres) > 0) { + foreach ($wheres as $field => $value) { + if (is_array($value)) { + if (isset($value['field'], $value['value'])) { + if (is_array($value['value'])) { + $this->db->where_in($value['field'], $value['value']); + } else { + $this->db->where($value['field'] . ' ' . trim($value['operator']), $value['value']); + } + } else { + $this->db->where_in($field, $value); + } + } else { + $this->db->where($field, $value); + } + } + } + + return $this->db; + } + + /** + * Function prepare_wheres_not_statement + * + * @param $wheres + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 50:13 + */ + public function prepare_wheres_not_statement($wheres) + { + if (!empty($wheres) && is_array($wheres) && count($wheres) > 0) { + foreach ($wheres as $field => $value) { + if (is_array($value)) { + if (isset($value['field'], $value['value'])) { + if (is_array($value['value'])) { + $this->db->where_not_in($value['field'], $value['value']); + } else { + $this->db->where($value['field'] . ' ' . trim($value['operator']), $value['value']); + } + } else { + $this->db->where_not_in($field, $value); + } + } else { + $this->db->where($field . $this->is_not, $value); + } + } + } + + return $this->db; + } + + /** + * Function only_status_is_active + * + * @param $act + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @throws \HungNG_CI_Exception + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 27/03/2023 15:20 + */ + public function only_status_is_active($act = true, $field = 'status', $table = '') + { + if ($act === true) { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix( + $this->tableName + ); + $useField = !empty($field) ? trim($field) : 'status'; + $tableExists = $this->db->table_exists($tableName); + if ($tableExists === false) { + throw new HungNG_CI_Exception('Table ' . $tableName . ' không tồn tại', 404); + } + $fieldExists = $this->db->field_exists($useField, $tableName); + if ($fieldExists === false) { + throw new HungNG_CI_Exception( + 'Field ' . $useField . ' không tìm thấy trong bảng ' . $tableName, 404 + ); + } + + return $this->db->where($tableName . '.' . $useField, self::DEFAULT_STATUS_IS_ACTIVE); + } + + return $this->db; + } + + /** + * Function only_status_is_de_active + * + * @param $act + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @throws \HungNG_CI_Exception + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 27/03/2023 17:52 + */ + public function only_status_is_de_active($act = true, $field = 'status', $table = '') + { + if ($act === true) { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix( + $this->tableName + ); + $useField = !empty($field) ? trim($field) : 'status'; + $tableExists = $this->db->table_exists($tableName); + if ($tableExists === false) { + throw new HungNG_CI_Exception('Table ' . $tableName . ' không tồn tại', 404); + } + $fieldExists = $this->db->field_exists($useField, $tableName); + if ($fieldExists === false) { + throw new HungNG_CI_Exception( + 'Field ' . $useField . ' không tìm thấy trong bảng ' . $tableName, 404 + ); + } + + return $this->db->where($tableName . '.' . $useField, self::DEFAULT_STATUS_IS_DE_ACTIVE); + } + + return $this->db; + } + + /** + * Function bind_recursive_from_category + * + * @param $allSubId + * @param $parentId + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 00:20 + */ + public function bind_recursive_from_category($allSubId, $parentId, $field = 'categoryId', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $listID = $this->build_list_id_with_parent_id($allSubId, $parentId); + if (is_array($listID)) { + $this->db->where_in($tableName . '.' . $field, $listID); + } else { + $this->db->where($tableName . '.' . $field, $listID); + } + + return $this->db; + } + + /** + * Function filter_by_primary_id + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 03:06 + */ + public function filter_by_primary_id($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + if ($id !== null) { + if (is_array($id)) { + $this->db->where_in($tableName . '.' . $field, $id); + } else { + $this->db->where($tableName . '.' . $field, $id); + } + } + + return $this->db; + } + + /** + * Function build_operator_equal_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 03:02 + */ + public function build_operator_equal_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + if ($id !== null) { + if (is_array($id)) { + $this->db->where_in($tableName . '.' . $field, $id); + } else { + $this->db->where($tableName . '.' . $field, $id); + } + } + + return $this->db; + } + + /** + * Function build_operator_not_equal_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 02:59 + */ + public function build_operator_not_equal_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + if ($id !== null) { + if (is_array($id)) { + $this->db->where_not_in($tableName . '.' . $field, $id); + } else { + $this->db->where($tableName . '.' . $field . $this->is_not, $id); + } + } + + return $this->db; + } + + /** + * Function build_operator_less_than_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 02:56 + */ + public function build_operator_less_than_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_LESS_THAN, $id); + + return $this->db; + } + + /** + * Function build_operator_greater_than_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 02:53 + */ + public function build_operator_greater_than_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_GREATER_THAN, $id); + + return $this->db; + } + + /** + * Function build_operator_less_than_or_equal_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 02:50 + */ + public function build_operator_less_than_or_equal_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_LESS_THAN_OR_EQUAL_TO, $id); + + return $this->db; + } + + /** + * Function build_operator_greater_than_or_equal_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 02:46 + */ + public function build_operator_greater_than_or_equal_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $id); + + return $this->db; + } + + /** + * Function build_operator_space_ship_to + * + * @param $id + * @param $field + * @param $table + * + * @return bool|\CI_DB_query_builder|object + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 02:41 + */ + public function build_operator_space_ship_to($id, $field = 'id', $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_IS_SPACESHIP, $id); + + return $this->db; + } + + // ------------------------------------------ Database Metadata ------------------------------------------ // + + /** + * Function list_tables + * + * @return array|false|string + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 21:09 + */ + public function list_tables() + { + return $this->db->list_tables(); + } + + /** + * Function table_exists + * + * @param $table + * + * @return bool + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 25:08 + */ + public function table_exists($table) + { + return $this->db->table_exists($table); + } + + /** + * Function list_fields_on_table + * + * @param $table + * + * @return array|false|string + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 25:23 + */ + public function list_fields_on_table($table) + { + return $this->db->list_fields($table); + } + + /** + * Function field_exists_on_table + * + * @param $field + * @param $table + * + * @return bool + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 26:47 + */ + public function field_exists_on_table($field, $table) + { + return $this->db->field_exists($field, $table); + } + + /** + * Function list_all_field_data + * + * @param $table + * + * @return array|false + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 26:44 + */ + public function list_all_field_data($table) + { + return $this->db->field_data($table); + } + + // ---------------------------------------------------------------------------------------------------------------------------------------- // + + /** + * Function check_exists + * + * @param string $value + * @param mixed $field + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 30:20 + */ + public function check_exists($value = '', $field = null) + { + $this->db->select($this->primary_key); + $this->db->from($this->tableName); + if ($field === null) { + $this->db->where($this->primary_key, $value); + } else { + $this->db->where($field, $value); + } + + return $this->db->count_all_results(); + } + + /** + * Function get_last_id + * + * @param string $field + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 30:12 + */ + public function get_last_id($field = 'id') + { + $this->db->select($field); + $this->db->from($this->tableName); + $this->db->limit(1); + $this->db->order_by($field, 'DESC'); + $row = $this->db->get()->row(); + if (is_object($row)) { + return $row->$field; + } + + return 0; + } + + /** + * Function get_first_id + * + * @param $field + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 08:37 + */ + public function get_first_id($field = 'id') + { + $this->db->select($field); + $this->db->from($this->tableName); + $this->db->limit(1); + $this->db->order_by($field, 'ASC'); + $row = $this->db->get()->row(); + if (is_object($row)) { + return $row->$field; + } + + return 0; + } + + /** + * Function get_random_id + * + * @param $field + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/02/2023 08:41 + */ + public function get_random_id($field = 'id') + { + $this->db->select($field); + $this->db->from($this->tableName); + $this->db->limit(1); + $this->db->order_by($field, 'RANDOM'); + $row = $this->db->get()->row(); + if (is_object($row)) { + return $row->$field; } + + return 0; + } + + /** + * Function get_all + * + * @param string $field + * + * @return array|array[]|object|object[] + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 30:08 + */ + public function get_all($field = '*') + { + $this->db->select($field); + $this->db->from($this->tableName); + + return $this->db->get()->result(); + } + + /** + * Function get_all_asc + * + * @param $field + * + * @return array|array[]|object|object[] + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 19:08 + */ + public function get_all_asc($field = '*') + { + $this->db->select($field); + $this->db->from($this->tableName); + $this->db->order_by($field, 'ASC'); + + return $this->db->get()->result(); + } + + /** + * Function get_all_desc + * + * @param $field + * + * @return array|array[]|object|object[] + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 16/02/2023 19:03 + */ + public function get_all_desc($field = '*') + { + $this->db->select($field); + $this->db->from($this->tableName); + $this->db->order_by($field, 'DESC'); + + return $this->db->get()->result(); + } + + /** + * Function count_all + * + * @param string $field + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 30:00 + */ + public function count_all($field = '*') + { + $this->db->select($field); + $this->db->from($this->tableName); + + return $this->db->count_all_results(); + } + + /** + * Function count_all_from_table + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 09/13/2021 07:51 + */ + public function count_all_from_table() + { + return $this->db->count_all($this->tableName); + } + + /** + * Function count_all_by_wheres + * + * @param $wheres + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 27/03/2023 04:52 + */ + public function count_all_by_wheres($wheres) + { + return $this->prepare_wheres_statement($wheres)->count_all_results($this->tableName); + } + + /** + * Function count_all_by_not_wheres + * + * @param $wheres + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 27/03/2023 05:47 + */ + public function count_all_by_not_wheres($wheres) + { + return $this->prepare_wheres_not_statement($wheres)->count_all_results($this->tableName); + } + + /** + * Function get_list_distinct + * + * @param string $field + * + * @return array|array[]|object|object[] + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:58 + */ + public function get_list_distinct($field = '*') + { + $this->db->distinct(); + $this->db->select($field); + $this->db->from($this->tableName); + + return $this->db->get()->result(); } - /** - * Function setDb - * - * @param string $db_group - * - * @return $this - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 12/09/2020 38:49 - */ - public function setDb($db_group = '') - { - $this->db = $this->load->database($db_group, true, true); - - return $this; - } - - /** - * Function setTableName - * - * @param string $tableName - * - * @return $this - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 30:49 - */ - public function setTableName($tableName = '') - { - $this->tableName = $tableName; - - return $this; - } - - /** - * Function getTableName - * - * @return string - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 30:45 - */ - public function getTableName() - { - return $this->tableName; - } - - /** - * Function bindDBPrefix - * - * @param $table - * - * @return string - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 27/03/2023 53:42 - */ - public function bindDBPrefix($table) - { - if (!empty($this->db->dbprefix)) { - if (strpos($table, $this->db->dbprefix)) { - return $table; - } - } - - return $this->db->dbprefix($table); - } - - /** - * Function close - * - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/25/2021 52:39 - */ - public function close() - { - $this->db->close(); - } - - // ---------------------------------------------------------------------------------------------------------------------------------------- // - - /** - * Function get_off_set - * - * @param $size - * @param $page - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 22/03/2023 13:24 - */ - public function get_off_set($size = 500, $page = 0) - { - $size = (int)$size; - $page = (int)$page; - if ($page !== 0) { - if ($page <= 0 || empty($page)) { - $page = 1; - } - $start = ($page - 1) * $size; - } else { - $start = $page; - } - - return (int)$start; - } - - /** - * Function page_limit - * - * @param int $size - * @param int $page - * - * @return \CI_DB_query_builder - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 03/06/2022 20:38 - */ - public function page_limit($size = 500, $page = 0) - { - if ($size !== 'no_limit') { - $start = $this->get_off_set($size, $page); - - return $this->db->limit($size, $start); - } - - return $this->db; - } - - /** - * Function _page_limit alias of page_limit - * - * @param int $size - * @param int $page - * - * @return \CI_DB_query_builder - * @deprecated use page_limit method - * @author : 713uk13m - * @copyright : 713uk13m - * @time : 08/16/2021 30:54 - */ - public function _page_limit($size = 500, $page = 0) - { - return $this->page_limit($size, $page); - } - - /** - * Function bind_order_result - * - * Lưu ý: mặc định sẽ order theo giá trị $direction và $field, $field truyền vào - * Trong trường hợp sử dụng $table=order_by_field, cần setup cả table và field trong biến $order_by_field trước khi truyền vào để order cho đúng - * - * @param $order_by_field - * @param $direction - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 11/02/2023 49:18 - */ - public function bind_order_result($order_by_field, $direction = 'desc', $field = 'id', $table = '') - { - return $this->build_order_result($order_by_field, $direction, $field, $table); - } - - /** - * Function build_order_result - * - * Lưu ý: mặc định sẽ order theo giá trị $direction và $field, $field truyền vào - * Trong trường hợp sử dụng $table=order_by_field, cần setup cả table và field trong biến $order_by_field trước khi truyền vào để order cho đúng - * - * @param $order_by_field - * @param $direction - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 28:15 - */ - public function build_order_result($order_by_field, $direction = 'desc', $field = 'id', $table = '') - { - if (!empty($table)) { - $tableName = $this->bindDBPrefix(trim($table)) . '.'; - } else { - $tableName = $this->bindDBPrefix($this->tableName) . '.'; - } - if ($table === 'order_by_field') { - $tableName = ''; - } - if (isset($order_by_field) && is_array($order_by_field) && count($order_by_field) > 0) { - foreach ($order_by_field as $f) { - $this->db->order_by($tableName . $f['field_name'], $f['order_value']); - } - } else { - $direction = strtoupper(trim($direction)); - if ($direction === 'RANDOM') { - $this->db->order_by($tableName . $field, 'RANDOM'); - } else { - $this->db->order_by($tableName . $field, $direction); - } - } - - return $this->db; - } - - /** - * Function build_list_id_with_parent_id - Tạo 1 list các ID, trong đó chứa các tập con phụ thuộc của ID đ - * - * @param array|object|mixed $allSubId - * @param string|int $parentId - * - * @return array|string|int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 13:31 - */ - public function build_list_id_with_parent_id($allSubId, $parentId) - { - if (is_array($allSubId) || is_object($allSubId)) { - // Xác định lấy toàn bộ tin tức ở các category con - $countSub = count($allSubId); // Đếm bảng ghi Category con - if ($countSub) { - // Nếu tồn tại các category con - $listSub = array(); - $listSub[] = $parentId; // Push category cha - foreach ($allSubId as $item) { - $itemId = is_array($item) ? $item['id'] : $item->id; - $listSub[] = $itemId; // Push các category con vào mảng dữ liệu - } - - return $listSub; - } - } - - return $parentId; - } - - /** - * Function prepare_simple_wheres_not_statement - * - * @param $value - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 31:03 - */ - public function prepare_simple_wheres_not_statement($value, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - if ($value !== null) { - if (is_array($value)) { - $this->db->where_not_in($tableName . '.' . $field, $value); - } else { - $this->db->where($tableName . '.' . $field . $this->is_not, $value); - } - } - - return $this->db; - } - - /** - * Function prepare_simple_wheres_statement - * - * @param $value - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 31:11 - */ - public function prepare_simple_wheres_statement($value, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - if ($value !== null) { - if (is_array($value)) { - $this->db->where_in($tableName . '.' . $field, $value); - } else { - $this->db->where($tableName . '.' . $field, $value); - } - } - - return $this->db; - } - - /** - * Function prepare_wheres_statement - * - * @param $wheres - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 50:08 - */ - public function prepare_wheres_statement($wheres) - { - if (!empty($wheres) && is_array($wheres) && count($wheres) > 0) { - foreach ($wheres as $field => $value) { - if (is_array($value)) { - if (isset($value['field'], $value['value'])) { - if (is_array($value['value'])) { - $this->db->where_in($value['field'], $value['value']); - } else { - $this->db->where($value['field'] . ' ' . trim($value['operator']), $value['value']); - } - } else { - $this->db->where_in($field, $value); - } - } else { - $this->db->where($field, $value); - } - } - } - - return $this->db; - } - - /** - * Function prepare_wheres_not_statement - * - * @param $wheres - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 50:13 - */ - public function prepare_wheres_not_statement($wheres) - { - if (!empty($wheres) && is_array($wheres) && count($wheres) > 0) { - foreach ($wheres as $field => $value) { - if (is_array($value)) { - if (isset($value['field'], $value['value'])) { - if (is_array($value['value'])) { - $this->db->where_not_in($value['field'], $value['value']); - } else { - $this->db->where($value['field'] . ' ' . trim($value['operator']), $value['value']); - } - } else { - $this->db->where_not_in($field, $value); - } - } else { - $this->db->where($field . $this->is_not, $value); - } - } - } - - return $this->db; - } - - /** - * Function only_status_is_active - * - * @param $act - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @throws \HungNG_CI_Exception - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 27/03/2023 15:20 - */ - public function only_status_is_active($act = true, $field = 'status', $table = '') - { - if ($act === true) { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix( - $this->tableName - ); - $useField = !empty($field) ? trim($field) : 'status'; - $tableExists = $this->db->table_exists($tableName); - if ($tableExists === false) { - throw new HungNG_CI_Exception('Table ' . $tableName . ' không tồn tại', 404); - } - $fieldExists = $this->db->field_exists($useField, $tableName); - if ($fieldExists === false) { - throw new HungNG_CI_Exception( - 'Field ' . $useField . ' không tìm thấy trong bảng ' . $tableName, 404 - ); - } - - return $this->db->where($tableName . '.' . $useField, self::DEFAULT_STATUS_IS_ACTIVE); - } - - return $this->db; - } - - /** - * Function only_status_is_de_active - * - * @param $act - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @throws \HungNG_CI_Exception - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 27/03/2023 17:52 - */ - public function only_status_is_de_active($act = true, $field = 'status', $table = '') - { - if ($act === true) { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix( - $this->tableName - ); - $useField = !empty($field) ? trim($field) : 'status'; - $tableExists = $this->db->table_exists($tableName); - if ($tableExists === false) { - throw new HungNG_CI_Exception('Table ' . $tableName . ' không tồn tại', 404); - } - $fieldExists = $this->db->field_exists($useField, $tableName); - if ($fieldExists === false) { - throw new HungNG_CI_Exception( - 'Field ' . $useField . ' không tìm thấy trong bảng ' . $tableName, 404 - ); - } - - return $this->db->where($tableName . '.' . $useField, self::DEFAULT_STATUS_IS_DE_ACTIVE); - } - - return $this->db; - } - - /** - * Function bind_recursive_from_category - * - * @param $allSubId - * @param $parentId - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 00:20 - */ - public function bind_recursive_from_category($allSubId, $parentId, $field = 'categoryId', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $listID = $this->build_list_id_with_parent_id($allSubId, $parentId); - if (is_array($listID)) { - $this->db->where_in($tableName . '.' . $field, $listID); - } else { - $this->db->where($tableName . '.' . $field, $listID); - } - - return $this->db; - } - - /** - * Function filter_by_primary_id - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 03:06 - */ - public function filter_by_primary_id($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - if ($id !== null) { - if (is_array($id)) { - $this->db->where_in($tableName . '.' . $field, $id); - } else { - $this->db->where($tableName . '.' . $field, $id); - } - } - - return $this->db; - } - - /** - * Function build_operator_equal_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 03:02 - */ - public function build_operator_equal_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - if ($id !== null) { - if (is_array($id)) { - $this->db->where_in($tableName . '.' . $field, $id); - } else { - $this->db->where($tableName . '.' . $field, $id); - } - } - - return $this->db; - } - - /** - * Function build_operator_not_equal_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 02:59 - */ - public function build_operator_not_equal_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - if ($id !== null) { - if (is_array($id)) { - $this->db->where_not_in($tableName . '.' . $field, $id); - } else { - $this->db->where($tableName . '.' . $field . $this->is_not, $id); - } - } - - return $this->db; - } - - /** - * Function build_operator_less_than_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 02:56 - */ - public function build_operator_less_than_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_LESS_THAN, $id); - - return $this->db; - } - - /** - * Function build_operator_greater_than_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 02:53 - */ - public function build_operator_greater_than_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_GREATER_THAN, $id); - - return $this->db; - } - - /** - * Function build_operator_less_than_or_equal_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 02:50 - */ - public function build_operator_less_than_or_equal_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_LESS_THAN_OR_EQUAL_TO, $id); - - return $this->db; - } - - /** - * Function build_operator_greater_than_or_equal_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 02:46 - */ - public function build_operator_greater_than_or_equal_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $id); - - return $this->db; - } - - /** - * Function build_operator_space_ship_to - * - * @param $id - * @param $field - * @param $table - * - * @return bool|\CI_DB_query_builder|object - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 02:41 - */ - public function build_operator_space_ship_to($id, $field = 'id', $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->db->where($tableName . '.' . $field . ' ' . self::OPERATOR_IS_SPACESHIP, $id); - - return $this->db; - } - - // ------------------------------------------ Database Metadata ------------------------------------------ // - - /** - * Function list_tables - * - * @return array|false|string - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 21:09 - */ - public function list_tables() - { - return $this->db->list_tables(); - } - - /** - * Function table_exists - * - * @param $table - * - * @return bool - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 25:08 - */ - public function table_exists($table) - { - return $this->db->table_exists($table); - } - - /** - * Function list_fields_on_table - * - * @param $table - * - * @return array|false|string - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 25:23 - */ - public function list_fields_on_table($table) - { - return $this->db->list_fields($table); - } - - /** - * Function field_exists_on_table - * - * @param $field - * @param $table - * - * @return bool - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 26:47 - */ - public function field_exists_on_table($field, $table) - { - return $this->db->field_exists($field, $table); - } - - /** - * Function list_all_field_data - * - * @param $table - * - * @return array|false - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 26:44 - */ - public function list_all_field_data($table) - { - return $this->db->field_data($table); - } - - // ---------------------------------------------------------------------------------------------------------------------------------------- // - - /** - * Function check_exists - * - * @param string $value - * @param mixed $field - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 30:20 - */ - public function check_exists($value = '', $field = null) - { - $this->db->select($this->primary_key); - $this->db->from($this->tableName); - if ($field === null) { - $this->db->where($this->primary_key, $value); - } else { - $this->db->where($field, $value); - } - - return $this->db->count_all_results(); - } - - /** - * Function get_last_id - * - * @param string $field - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 30:12 - */ - public function get_last_id($field = 'id') - { - $this->db->select($field); - $this->db->from($this->tableName); - $this->db->limit(1); - $this->db->order_by($field, 'DESC'); - $row = $this->db->get()->row(); - if (is_object($row)) { - return $row->$field; - } - - return 0; - } - - /** - * Function get_first_id - * - * @param $field - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 08:37 - */ - public function get_first_id($field = 'id') - { - $this->db->select($field); - $this->db->from($this->tableName); - $this->db->limit(1); - $this->db->order_by($field, 'ASC'); - $row = $this->db->get()->row(); - if (is_object($row)) { - return $row->$field; - } - - return 0; - } - - /** - * Function get_random_id - * - * @param $field - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/02/2023 08:41 - */ - public function get_random_id($field = 'id') - { - $this->db->select($field); - $this->db->from($this->tableName); - $this->db->limit(1); - $this->db->order_by($field, 'RANDOM'); - $row = $this->db->get()->row(); - if (is_object($row)) { - return $row->$field; - } - - return 0; - } - - /** - * Function get_all - * - * @param string $field - * - * @return array|array[]|object|object[] - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 30:08 - */ - public function get_all($field = '*') - { - $this->db->select($field); - $this->db->from($this->tableName); - - return $this->db->get()->result(); - } - - /** - * Function get_all_asc - * - * @param $field - * - * @return array|array[]|object|object[] - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 19:08 - */ - public function get_all_asc($field = '*') - { - $this->db->select($field); - $this->db->from($this->tableName); - $this->db->order_by($field, 'ASC'); - - return $this->db->get()->result(); - } - - /** - * Function get_all_desc - * - * @param $field - * - * @return array|array[]|object|object[] - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 16/02/2023 19:03 - */ - public function get_all_desc($field = '*') - { - $this->db->select($field); - $this->db->from($this->tableName); - $this->db->order_by($field, 'DESC'); - - return $this->db->get()->result(); - } - - /** - * Function count_all - * - * @param string $field - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 30:00 - */ - public function count_all($field = '*') - { - $this->db->select($field); - $this->db->from($this->tableName); - - return $this->db->count_all_results(); - } - - /** - * Function count_all_from_table - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 09/13/2021 07:51 - */ - public function count_all_from_table() - { - return $this->db->count_all($this->tableName); - } - - /** - * Function count_all_by_wheres - * - * @param $wheres - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 27/03/2023 04:52 - */ - public function count_all_by_wheres($wheres) - { - return $this->prepare_wheres_statement($wheres)->count_all_results($this->tableName); - } - - /** - * Function count_all_by_not_wheres - * - * @param $wheres - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 27/03/2023 05:47 - */ - public function count_all_by_not_wheres($wheres) - { - return $this->prepare_wheres_not_statement($wheres)->count_all_results($this->tableName); - } - - /** - * Function get_list_distinct - * - * @param string $field - * - * @return array|array[]|object|object[] - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:58 - */ - public function get_list_distinct($field = '*') - { - $this->db->distinct(); - $this->db->select($field); - $this->db->from($this->tableName); - - return $this->db->get()->result(); - } - - /** - * Function get_data_simple_result - * - * @param string $select - * @param array $wheres - * @param int $size - * @param int $page - * @param string[] $orderBy - * - * @return array|array[]|object|object[] - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 09/13/2021 16:49 - */ - public function get_data_simple_result( - $select = '*', - $wheres = array(), - $size = 75, - $page = 0, - $orderBy = array('id' => 'DESC') - ) { - $tableName = $this->bindDBPrefix($this->tableName); - $this->db->select($select); - $this->db->from($this->tableName); - if (count($wheres) > 0) { - foreach ($wheres as $field => $value) { - if (is_array($value)) { - $this->db->where_in($tableName . '.' . $field, $value); - } else { - $this->db->where($tableName . '.' . $field, $value); - } - } - } - - foreach ($orderBy as $orderField => $orderDirection) { - $this->db->order_by($tableName . '.' . $orderField, $orderDirection); - } - - $this->page_limit($size, $page); - - return $this->db->get()->result(); - } - - /** - * Function get_all_data_simple_result - * - * @param mixed $options - * - * @return array|array[]|object|object[] - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 25/11/2021 46:10 - */ - public function get_all_data_simple_result($options = null) - { - $this->db->from($this->tableName); - if (is_array($options)) { - foreach ($options as $field => $value) { - if (is_array($value)) { - $this->db->where_in($field, $value); - } else { - $this->db->where($field, $value); - } - } - } - - return $this->db->get()->result(); - } - - /** - * Function get_info - * - * @param string $value - * @param mixed $field - * @param bool $array - * - * @return array|mixed|object|null - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:55 - */ - public function get_info($value = '', $field = null, $array = false) - { - $this->db->from($this->tableName); - if ($field === null) { - $this->db->where($this->primary_key, $value); - } else { - $this->db->where($field, $value); - } - if ($array === true) { - return $this->db->get()->row_array(); - } - - return $this->db->get()->row(); - } - - /** - * Function get_value - * - * @param string $value_input - * @param mixed $field_input - * @param mixed $field_output - * - * @return array|mixed|object|null - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:51 - */ - public function get_value($value_input = '', $field_input = null, $field_output = null) - { - if (null !== $field_output) { - $this->db->select($field_output); - } - $this->db->from($this->tableName); - if ($field_input === null) { - $this->db->where($this->primary_key, $value_input); - } else { - $this->db->where($field_input, $value_input); - } - $query = $this->db->get(); - if (null !== $field_output) { - if (null === $query->row()) { - return null; - } - - return $query->row()->$field_output; - } - - return $query->row(); - } - - /** - * Function add - * - * @param array $data - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:47 - */ - public function add($data = array()) - { - $this->db->insert($this->tableName, $data); - - return $this->db->insert_id(); - } - - /** - * Function insert_batch - * - * @param array $data - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:44 - */ - public function insert_batch($data = array()) - { - $this->db->insert_batch($this->tableName, $data); - - return $this->db->insert_id(); - } - - /** - * Function update - * - * @param string $id - * @param array $data - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:32 - */ - public function update($id = '', $data = array()) - { - if (empty($id)) { - log_message('error', 'Update method give Input Primary Key is Empty'); - - return 0; - } - $this->db->where($this->primary_key, $id); - $this->db->update($this->tableName, $data); - - return $this->db->affected_rows(); - } - - /** - * Function where_update - * - * @param $wheres - * @param $data - * @param $table - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 22/03/2023 32:40 - */ - public function where_update($wheres, $data, $table = '') - { - if (empty($wheres)) { - log_message('error', 'Update method give Input Wheres is Empty'); - - return 0; - } - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->prepare_wheres_statement($wheres); - $this->db->update($tableName, $data); - - return $this->db->affected_rows(); - } - - /** - * Function delete - * - * @param string $id - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 09/20/2021 38:36 - */ - public function delete($id = '') - { - if (empty($id)) { - log_message('error', 'Delete method give Input Primary Key is Empty'); - - return 0; - } - $this->db->where($this->primary_key, $id); - $this->db->delete($this->tableName); - - return $this->db->affected_rows(); - } - - /** - * Function where_delete - * - * @param $wheres - * @param $data - * @param $table - * - * @return int - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 22/03/2023 33:27 - */ - public function where_delete($wheres, $data, $table = '') - { - if (empty($wheres)) { - log_message('error', 'Delete method give Input Wheres is Empty'); - - return 0; - } - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - $this->prepare_wheres_statement($wheres); - $this->db->delete($tableName, $data); - - return $this->db->affected_rows(); - } - - /** - * Function request_builder - * - * @param $search - * @param $table - * - * @author : 713uk13m - * @copyright: 713uk13m - * @time : 08/16/2021 29:37 - */ - public function request_builder($search, $table = '') - { - $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); - if (!empty($search)) { - foreach ($search as $field => $value) { - if (!empty($value) && $this->db->field_exists($field, $tableName)) { - if (is_array($value)) { - $this->db->where_in($tableName . '.' . $field, $value); - } else { - $this->db->like($tableName . '.' . $field, $value); - } - } - if ($field === 'sort') { - $sort = strpos($value, '-') === false ? 'DESC' : 'ASC'; - $column = (strpos($value, '-') === false) ? $value : substr($value, 1); - if ($this->db->field_exists($column, $tableName)) { - $this->db->order_by($tableName . '.' . $column, $sort); - } - } - } - } - } - } + /** + * Function get_data_simple_result + * + * @param string $select + * @param array $wheres + * @param int $size + * @param int $page + * @param string[] $orderBy + * + * @return array|array[]|object|object[] + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 09/13/2021 16:49 + */ + public function get_data_simple_result( + $select = '*', + $wheres = array(), + $size = 75, + $page = 0, + $orderBy = array('id' => 'DESC') + ) { + $tableName = $this->bindDBPrefix($this->tableName); + $this->db->select($select); + $this->db->from($this->tableName); + if (count($wheres) > 0) { + foreach ($wheres as $field => $value) { + if (is_array($value)) { + $this->db->where_in($tableName . '.' . $field, $value); + } else { + $this->db->where($tableName . '.' . $field, $value); + } + } + } + + foreach ($orderBy as $orderField => $orderDirection) { + $this->db->order_by($tableName . '.' . $orderField, $orderDirection); + } + + $this->page_limit($size, $page); + + return $this->db->get()->result(); + } + + /** + * Function get_all_data_simple_result + * + * @param mixed $options + * + * @return array|array[]|object|object[] + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 25/11/2021 46:10 + */ + public function get_all_data_simple_result($options = null) + { + $this->db->from($this->tableName); + if (is_array($options)) { + foreach ($options as $field => $value) { + if (is_array($value)) { + $this->db->where_in($field, $value); + } else { + $this->db->where($field, $value); + } + } + } + + return $this->db->get()->result(); + } + + /** + * Function get_info + * + * @param string $value + * @param mixed $field + * @param bool $array + * + * @return array|mixed|object|null + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:55 + */ + public function get_info($value = '', $field = null, $array = false) + { + $this->db->from($this->tableName); + if ($field === null) { + $this->db->where($this->primary_key, $value); + } else { + $this->db->where($field, $value); + } + if ($array === true) { + return $this->db->get()->row_array(); + } + + return $this->db->get()->row(); + } + + /** + * Function get_value + * + * @param string $value_input + * @param mixed $field_input + * @param mixed $field_output + * + * @return array|mixed|object|null + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:51 + */ + public function get_value($value_input = '', $field_input = null, $field_output = null) + { + if (null !== $field_output) { + $this->db->select($field_output); + } + $this->db->from($this->tableName); + if ($field_input === null) { + $this->db->where($this->primary_key, $value_input); + } else { + $this->db->where($field_input, $value_input); + } + $query = $this->db->get(); + if (null !== $field_output) { + if (null === $query->row()) { + return null; + } + + return $query->row()->$field_output; + } + + return $query->row(); + } + + /** + * Function add + * + * @param array $data + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:47 + */ + public function add($data = array()) + { + $this->db->insert($this->tableName, $data); + + return $this->db->insert_id(); + } + + /** + * Function insert_batch + * + * @param array $data + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:44 + */ + public function insert_batch($data = array()) + { + $this->db->insert_batch($this->tableName, $data); + + return $this->db->insert_id(); + } + + /** + * Function update + * + * @param string $id + * @param array $data + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:32 + */ + public function update($id = '', $data = array()) + { + if (empty($id)) { + log_message('error', 'Update method give Input Primary Key is Empty'); + + return 0; + } + $this->db->where($this->primary_key, $id); + $this->db->update($this->tableName, $data); + + return $this->db->affected_rows(); + } + + /** + * Function where_update + * + * @param $wheres + * @param $data + * @param $table + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 22/03/2023 32:40 + */ + public function where_update($wheres, $data, $table = '') + { + if (empty($wheres)) { + log_message('error', 'Update method give Input Wheres is Empty'); + + return 0; + } + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->prepare_wheres_statement($wheres); + $this->db->update($tableName, $data); + + return $this->db->affected_rows(); + } + + /** + * Function delete + * + * @param string $id + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 09/20/2021 38:36 + */ + public function delete($id = '') + { + if (empty($id)) { + log_message('error', 'Delete method give Input Primary Key is Empty'); + + return 0; + } + $this->db->where($this->primary_key, $id); + $this->db->delete($this->tableName); + + return $this->db->affected_rows(); + } + + /** + * Function where_delete + * + * @param $wheres + * @param $data + * @param $table + * + * @return int + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 22/03/2023 33:27 + */ + public function where_delete($wheres, $data, $table = '') + { + if (empty($wheres)) { + log_message('error', 'Delete method give Input Wheres is Empty'); + + return 0; + } + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + $this->prepare_wheres_statement($wheres); + $this->db->delete($tableName, $data); + + return $this->db->affected_rows(); + } + + /** + * Function request_builder + * + * @param $search + * @param $table + * + * @author : 713uk13m + * @copyright: 713uk13m + * @time : 08/16/2021 29:37 + */ + public function request_builder($search, $table = '') + { + $tableName = !empty($table) ? $this->bindDBPrefix(trim($table)) : $this->bindDBPrefix($this->tableName); + if (!empty($search)) { + foreach ($search as $field => $value) { + if (!empty($value) && $this->db->field_exists($field, $tableName)) { + if (is_array($value)) { + $this->db->where_in($tableName . '.' . $field, $value); + } else { + $this->db->like($tableName . '.' . $field, $value); + } + } + if ($field === 'sort') { + $sort = strpos($value, '-') === false ? 'DESC' : 'ASC'; + $column = (strpos($value, '-') === false) ? $value : substr($value, 1); + if ($this->db->field_exists($column, $tableName)) { + $this->db->order_by($tableName . '.' . $column, $sort); + } + } + } + } + } + } } diff --git a/hungng/HungNG_Model.php b/hungng/HungNG_Model.php index 06c9cd5..b1e2645 100644 --- a/hungng/HungNG_Model.php +++ b/hungng/HungNG_Model.php @@ -2,1986 +2,1997 @@ defined('BASEPATH') or exit('No direct script access allowed'); if (!class_exists('HungNG_Model')) { - /** - * Created by PhpStorm. - * User: hungna - * Date: 3/16/2017 - * Time: 1:59 PM - */ - /* - * Copyright (C) 2014 @avenirer [avenir.ro@gmail.com] - * Everyone is permitted to copy and distribute verbatim or modified copies of this license document, - * and changing it is allowed as long as the name is changed. - * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - * - ***** Do whatever you like with the original work, just don't be a dick. - ***** Being a dick includes - but is not limited to - the following instances: - ********* 1a. Outright copyright infringement - Don't just copy this and change the name. - ********* 1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick. - ********* 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick. - ***** If you become rich through modifications, related works/services, or supporting the original work, share the love. Only a dick would make loads off this work and not buy the original works creator(s) a pint. - ***** Code is provided with no warranty. - *********** Using somebody else's code and bitching when it goes wrong makes you a DONKEY dick. - *********** Fix the problem yourself. A non-dick would submit the fix back. - * - */ - - /** how to extend MY_Model: - * class User_model extends MY_Model - * { - * public $table = 'users'; // Set the name of the table for this model. - * public $primary_key = 'id'; // Set the primary key - * public $fillable = array(); // You can set an array with the fields that can be filled by insert/update - * public $protected = array(); // ...Or you can set an array with the fields that cannot be filled by - * insert/update public function __construct() - * { - * $this->_database_connection = group_name or array() | OPTIONAL - * Sets the connection preferences (group name) set up in the database.php. If not trset, it will use the - * 'default' (the $active_group) database connection. - * $this->timestamps = TRUE | array('made_at','modified_at','removed_at') - * If set to TRUE tells MY_Model that the table has 'created_at','updated_at' (and 'deleted_at' if - * $this->soft_delete is set to TRUE) If given an array as parameter, it tells MY_Model, that the first - * element is a created_at field type, the second element is a updated_at field type (and the third - * element is a deleted_at field type) - * $this->soft_deletes = FALSE; - * Enables (TRUE) or disables (FALSE) the "soft delete" on records. Default is FALSE - * $this->timestamps_format = 'Y-m-d H:i:s' - * You can at any time change the way the timestamp is created (the default is the MySQL standard datetime - * format) by modifying this variable. You can choose between whatever format is acceptable by the php - * function date() (default is 'Y-m-d H:i:s'), or 'timestamp' (UNIX timestamp) - * $this->return_as = 'object' | 'array' - * Allows the model to return the results as object or as array - * $this->has_one['phone'] = 'Phone_model' or $this->has_one['phone'] = - * array('Phone_model','foreign_key','local_key'); - * $this->has_one['address'] = 'Address_model' or $this->has_one['address'] = - * array('Address_model','foreign_key','another_local_key'); Allows establishing ONE TO ONE or more ONE TO ONE - * relationship(s) between models/tables - * $this->has_many['posts'] = 'Post_model' or $this->has_many['posts'] = - * array('Posts_model','foreign_key','another_local_key'); Allows establishing ONE TO MANY or more ONE TO MANY - * relationship(s) between models/tables - * $this->has_many_pivot['posts'] = 'Post_model' or $this->has_many_pivot['posts'] = - * array('Posts_model','foreign_primary_key','local_primary_key'); Allows establishing MANY TO MANY or more - * MANY TO MANY relationship(s) between models/tables with the use of a PIVOT TABLE - * !ATTENTION: The pivot table name must be composed of the two table names separated by "_" the table - * names having to to be alphabetically ordered (NOT users_posts, but posts_users). Also the pivot table - * must contain as identifying columns the columns named by convention as follows: table_name_singular + _ - * + foreign_table_primary_key. For example: considering that a post can have multiple authors, a pivot - * table that connects two tables (users and posts) must be named posts_users and must have post_id and - * user_id as identifying columns for the posts.id and users.id tables. - * $this->cache_driver = 'file' - * $this->cache_prefix = 'mm' - * If you know you will do some caching of results without the native caching solution, you can at any - * time use the MY_Model's caching. By default, MY_Model uses the files to cache result. If you want to - * change the way it stores the cache, you can change the $cache_driver property to whatever CodeIgniter - * cache driver you want to use. Also, with $cache_prefix, you can prefix the name of the caches. by - * default any cache made by MY_Model starts with 'mm' + _ + "name chosen for cache" - * $this->delete_cache_on_save = FALSE - * If you use caching often and you don't want to be forced to delete cache manually, you can enable - * $this->delete_cache_on_save by setting it to TRUE. If set to TRUE the model will auto-delete all cache - * related to the model's table whenever you write/update/delete data from that table. - * $this->pagination_delimiters = array('',''); - * If you know you will use the paginate() method, you can change the delimiters between the pages links - * $this->pagination_arrows = array('<','>'); - * You can also change the way the previous and next arrows look like. - * - * - * parent::__construct(); - * } - * } - * - * @property \CI_Loader load - * @property \CI_Form_validation form_validation - **/ - class HungNG_Model extends CI_Model - { - /** - * Select the database connection from the group names defined inside the database.php configuration file or an - * array. - */ - protected $_database_connection = null; - /** @var - * This one will hold the database connection object - */ - protected $_database; - /** @var null - * Sets table name - */ - public $table = null; - /** - * @var null - * Sets PRIMARY KEY - */ - public $primary_key = 'id'; - /** - * @var array - * You can establish the fields of the table. If you won't these fields will be filled by MY_Model (with one query) - */ - public $table_fields = array(); - /** - * @var array - * Sets fillable fields - */ - public $fillable = array(); - /** - * @var array - * Sets protected fields - */ - public $protected = array(); - private $_can_be_filled = null; - /** @var bool | array - * Enables created_at and updated_at fields - */ - protected $timestamps = true; - protected $timestamps_format = 'Y-m-d H:i:s'; - protected $_created_at_field; - protected $_updated_at_field; - protected $_deleted_at_field; - /** @var bool - * Enables soft_deletes - */ - protected $soft_deletes = false; - /** relationships variables */ - private $_relationships = array(); - public $has_one = array(); - public $has_many = array(); - public $has_many_pivot = array(); - public $separate_subqueries = true; - private $_requested = array(); - /** end relationships variables */ - /*caching*/ - public $cache_driver = 'file'; - public $cache_prefix = 'mm'; - protected $_cache = array(); - public $delete_cache_on_save = false; - /*pagination*/ - public $next_page; - public $previous_page; - public $all_pages; - public $pagination_delimiters; - public $pagination_arrows; - /* validation */ - private $validated = true; - private $row_fields_to_update = array(); - /** - * The various callbacks available to the model. Each are - * simple lists of method names (methods will be run on $this). - */ - protected $before_create = array(); - protected $after_create = array(); - protected $before_update = array(); - protected $after_update = array(); - protected $before_get = array(); - protected $after_get = array(); - protected $before_delete = array(); - protected $after_delete = array(); - protected $before_soft_delete = array(); - protected $after_soft_delete = array(); - protected $callback_parameters = array(); - protected $return_as = 'object'; - protected $return_as_dropdown = null; - protected $_dropdown_field = ''; - private $_trashed = 'without'; - private $_select = '*'; - - public function __construct() - { - parent::__construct(); - - log_message('info', 'HungNG_Model Class Initialized'); - - $this->load->helper('inflector'); - $this->_set_connection(); - $this->_set_timestamps(); - $this->_fetch_table(); - $this->pagination_delimiters = (isset($this->pagination_delimiters)) ? $this->pagination_delimiters : array( - '', - '' - ); - $this->pagination_arrows = (isset($this->pagination_arrows)) ? $this->pagination_arrows : array( - '<', - '>' - ); - /* These below are implementation examples for before_create and before_update triggers. - Their respective functions - add_creator() and add_updater() - can be found at the end of the model. - They add user id on create and update. If you comment this out don't forget to do the same for the methods() - $this->before_create[]='add_creator'; - $this->before_update[]='add_updater'; - */ - } - - public function _get_table_fields() - { - if (empty($this->table_fields)) { - $this->table_fields = $this->_database->list_fields($this->table); - } - - return true; - } - - public function fillable_fields() - { - if (!isset($this->_can_be_filled)) { - $this->_get_table_fields(); - $no_protection = array(); - foreach ($this->table_fields as $field) { - if (!in_array($field, $this->protected)) { - $no_protection[] = $field; - } - } - if (!empty($this->fillable)) { - $can_fill = array(); - foreach ($this->fillable as $field) { - if (in_array($field, $no_protection)) { - $can_fill[] = $field; - } - } - $this->_can_be_filled = $can_fill; - } else { - $this->_can_be_filled = $no_protection; - } - } - - return true; - } - - public function _prep_before_write($data) - { - $this->fillable_fields(); - // We make sure we have the fields that can be filled - $can_fill = $this->_can_be_filled; - // Let's make sure we receive an array... - $data_as_array = (is_object($data)) ? (array)$data : $data; - $new_data = array(); - $multi = $this->is_multidimensional($data); - if ($multi === false) { - foreach ($data_as_array as $field => $value) { - if (in_array($field, $can_fill)) { - $new_data[$field] = $value; - } else { - show_error('MY_Model: Unknown column (' . $field . ') in table: (' . $this->table . ').'); - } - } - } else { - foreach ($data_as_array as $key => $row) { - foreach ($row as $field => $value) { - if (in_array($field, $can_fill)) { - $new_data[$key][$field] = $value; - } else { - show_error('MY_Model: Unknown column ' . $field . ' in table: ' . $this->table); - } - } - } - } - - return $new_data; - } - - /* - * public function _prep_after_write() - * this function simply deletes the cache related to the model's table if $this->delete_cache_on_save is set to TRUE - * It should be called by any "save" method - */ - public function _prep_after_write() - { - if ($this->delete_cache_on_save === true) { - $this->delete_cache('*'); - } - - return true; - } - - public function _prep_before_read() - { - } - - public function _prep_after_read($data, $multi = true) - { - // let's join the subqueries... - $data = $this->join_temporary_results($data); - $this->_database->reset_query(); - $this->_requested = array(); - if (isset($this->return_as_dropdown) && $this->return_as_dropdown == 'dropdown') { - foreach ($data as $row) { - $dropdown[$row[$this->primary_key]] = $row[$this->_dropdown_field]; - } - $data = $dropdown; - $this->return_as_dropdown = null; - } elseif ($this->return_as == 'object') { - $data = json_decode(json_encode($data), false); - } - if (isset($this->_select)) { - $this->_select = '*'; - } - - return $data; - } - - /** - * public function from_form($rules = NULL,$additional_values = array(), $row_fields_to_update = array()) - * Gets data from form, after validating it and waits for an insert() or update() method in the query chain - * - * @param null $rules Gets the validation rules. If nothing is passed (NULL), will look for the - * validation rules inside the model $rules public property - * @param array $additional_values Accepts additional fields to be filled, fields that are not to be found - * inside - * the form. The values are inserted as an array with "field_name" => - * "field_value" - * @param array $row_fields_to_update You can mention the fields from the form that can be used to identify - * the row when doing an update - * - * @return $this - */ - public function from_form($rules = null, $additional_values = null, $row_fields_to_update = array()) - { - $this->_get_table_fields(); - $this->load->library('form_validation'); - if (!isset($rules)) { - if (empty($row_fields_to_update)) { - $rules = $this->rules['insert']; - } else { - $rules = $this->rules['update']; - } - } - $this->form_validation->set_rules($rules); - if ($this->form_validation->run()) { - $this->fillable_fields(); - $this->validated = array(); - foreach ($rules as $rule) { - if (in_array($rule['field'], $this->_can_be_filled)) { - $this->validated[$rule['field']] = $this->input->post($rule['field']); - } - } - if (isset($additional_values) && is_array($additional_values) && !empty($additional_values)) { - foreach ($additional_values as $field => $value) { - if (in_array($field, $this->_can_be_filled)) { - $this->validated[$field] = $value; - } - } - } - if (!empty($row_fields_to_update)) { - foreach ($row_fields_to_update as $key => $field) { - if (in_array($field, $this->table_fields)) { - $this->row_fields_to_update[$field] = $this->input->post($field); - } elseif (in_array($key, $this->table_fields)) { - $this->row_fields_to_update[$key] = $field; - } else { - continue; - } - } - } - - return $this; - } else { - $this->validated = false; - - return $this; - } - } - - /** - * public function insert($data) - * Inserts data into table. Can receive an array or a multidimensional array depending on what kind of insert we're - * talking about. - * - * @param $data - * - * @return int/array Returns id/ids of inserted rows - */ - public function insert($data = null) - { - if (!isset($data) && $this->validated != false) { - $data = $this->validated; - $this->validated = false; - } elseif (!isset($data)) { - return false; - } - $data = $this->_prep_before_write($data); - //now let's see if the array is a multidimensional one (multiple rows insert) - $multi = $this->is_multidimensional($data); - // if the array is not a multidimensional one... - if ($multi === false) { - if ($this->timestamps !== false) { - $data[$this->_created_at_field] = $this->_the_timestamp(); - } - $data = $this->trigger('before_create', $data); - if ($this->_database->insert($this->table, $data)) { - $this->_prep_after_write(); - $id = $this->_database->insert_id(); - $return = $this->trigger('after_create', $id); - - return $return; - } - - return false; - } // else... - else { - $return = array(); - foreach ($data as $row) { - if ($this->timestamps !== false) { - $row[$this->_created_at_field] = $this->_the_timestamp(); - } - $row = $this->trigger('before_create', $row); - if ($this->_database->insert($this->table, $row)) { - $return[] = $this->_database->insert_id(); - } - } - $this->_prep_after_write(); - $after_create = array(); - foreach ($return as $id) { - $after_create[] = $this->trigger('after_create', $id); - } - - return $after_create; - } - - return false; - } - - /* - * public function is_multidimensional($array) - * Verifies if an array is multidimensional or not; - * @param array $array - * @return bool return TRUE if the array is a multidimensional one - */ - public function is_multidimensional($array) - { - if (is_array($array)) { - foreach ($array as $element) { - if (is_array($element)) { - return true; - } - } - } - - return false; - } - - /** - * public function update($data) - * Updates data into table. Can receive an array or a multidimensional array depending on what kind of update we're - * talking about. - * - * @param array $data - * @param array|int $column_name_where - * @param bool $escape should the values be escaped or not - defaults to true - * - * @return str/array Returns id/ids of inserted rows - */ - public function update($data = null, $column_name_where = null, $escape = true) - { - if (!isset($data) && $this->validated != false) { - $data = $this->validated; - $this->validated = false; - } elseif (!isset($data)) { - $this->_database->reset_query(); - - return false; - } - // Prepare the data... - $data = $this->_prep_before_write($data); - //now let's see if the array is a multidimensional one (multiple rows insert) - $multi = $this->is_multidimensional($data); - // if the array is not a multidimensional one... - if ($multi === false) { - if ($this->timestamps !== false) { - $data[$this->_updated_at_field] = $this->_the_timestamp(); - } - $data = $this->trigger('before_update', $data); - if ($this->validated === false && count($this->row_fields_to_update)) { - $this->where($this->row_fields_to_update); - $this->row_fields_to_update = array(); - } - if (isset($column_name_where)) { - if (is_array($column_name_where)) { - $this->where($column_name_where); - } elseif (is_numeric($column_name_where)) { - $this->_database->where($this->primary_key, $column_name_where); - } else { - $column_value = (is_object($data)) ? $data->{$column_name_where} : $data[$column_name_where]; - $this->_database->where($column_name_where, $column_value); - } - } - if ($escape) { - if ($this->_database->update($this->table, $data)) { - $this->_prep_after_write(); - $affected = $this->_database->affected_rows(); - $return = $this->trigger('after_update', $affected); - - return $return; - } - } else { - if ($this->_database->set($data, null, false)->update($this->table)) { - $this->_prep_after_write(); - $affected = $this->_database->affected_rows(); - $return = $this->trigger('after_update', $affected); - - return $return; - } - } - - return false; - } // else... - else { - $rows = 0; - foreach ($data as $row) { - if ($this->timestamps !== false) { - $row[$this->_updated_at_field] = $this->_the_timestamp(); - } - $row = $this->trigger('before_update', $row); - if (is_array($column_name_where)) { - $this->_database->where($column_name_where[0], $column_name_where[1]); - } else { - $column_value = (is_object($row)) ? $row->{$column_name_where} : $row[$column_name_where]; - $this->_database->where($column_name_where, $column_value); - } - if ($escape) { - if ($this->_database->update($this->table, $row)) { - $rows++; - } - } else { - if ($this->_database->set($row, null, false)->update($this->table)) { - $rows++; - } - } - } - $affected = $rows; - $this->_prep_after_write(); - $return = $this->trigger('after_update', $affected); - - return $return; - } - - return false; - } - - /** - * public function where($field_or_array = NULL, $operator_or_value = NULL, $value = NULL, $with_or = FALSE, - * $with_not = FALSE, $custom_string = FALSE) Sets a where method for the $this object - * - * @param null $field_or_array - can receive a field name or an array with more wheres... - * @param null $operator_or_value - can receive a database operator or, if it has a field, the value to equal with - * @param null $value - a value if it received a field name and an operator - * @param bool $with_or - if set to true will create a or_where query type pr a or_like query type, - * depending on the operator - * @param bool $with_not - if set to true will also add "NOT" in the where - * @param bool $custom_string - if set to true, will simply assume that $field_or_array is actually a string - * and pass it to the where query - * - * @return $this - */ - public function where( - $field_or_array = null, - $operator_or_value = null, - $value = null, - $with_or = false, - $with_not = false, - $custom_string = false - ) { - if ($this->soft_deletes === true) { - $backtrace = debug_backtrace(); #fix for lower PHP 5.4 version - if ($backtrace[1]['function'] != 'force_delete') { - $this->_where_trashed(); - } - } - if (is_array($field_or_array)) { - $multi = $this->is_multidimensional($field_or_array); - if ($multi === true) { - foreach ($field_or_array as $where) { - $field = $where[0]; - $operator_or_value = isset($where[1]) ? $where[1] : null; - $value = isset($where[2]) ? $where[2] : null; - $with_or = (isset($where[3])) ? true : false; - $with_not = (isset($where[4])) ? true : false; - $this->where($field, $operator_or_value, $value, $with_or, $with_not); - } - - return $this; - } - } - if ($with_or === true) { - $where_or = 'or_where'; - } else { - $where_or = 'where'; - } - if ($with_not === true) { - $not = '_not'; - } else { - $not = ''; - } - if ($custom_string === true) { - $this->_database->{$where_or}($field_or_array, null, false); - } elseif (is_numeric($field_or_array)) { - $this->_database->{$where_or}(array($this->table . '.' . $this->primary_key => $field_or_array)); - } elseif (is_array($field_or_array) && !isset($operator_or_value)) { - $this->_database->where($field_or_array); - } elseif (!isset($value) && isset($field_or_array) && isset($operator_or_value) && !is_array( - $operator_or_value - )) { - $this->_database->{$where_or}(array($this->table . '.' . $field_or_array => $operator_or_value)); - } elseif (!isset($value) && isset($field_or_array) && isset($operator_or_value) && is_array( - $operator_or_value - ) && !is_array($field_or_array)) { - //echo $field_or_array; - //exit; - $this->_database->{$where_or . $not . '_in'}($this->table . '.' . $field_or_array, $operator_or_value); - } elseif (isset($field_or_array) && isset($operator_or_value) && isset($value)) { - $operator_or_value = bear_str_to_lower($operator_or_value); - if ($operator_or_value === 'like') { - if ($with_not === true) { - $like = 'not_like'; - } else { - $like = 'like'; - } - if ($with_or === true) { - $like = 'or_' . $like; - } - $this->_database->{$like}($field_or_array, $value); - } else { - $this->_database->{$where_or}($field_or_array . ' ' . $operator_or_value, $value); - } - } - - return $this; - } - - /** - * public function limit($limit, $offset = 0) - * Sets a rows limit to the query - * - * @param $limit - * @param int $offset - * - * @return $this - */ - public function limit($limit, $offset = 0) - { - $this->_database->limit($limit, $offset); - - return $this; - } - - /** - * public function group_by($grouping_by) - * A wrapper to $this->_database->group_by() - * - * @param $grouping_by - * - * @return $this - */ - public function group_by($grouping_by) - { - $this->_database->group_by($grouping_by); - - return $this; - } - - /** - * public function delete($where) - * Deletes data from table. - * - * @param $where primary_key(s) Can receive the primary key value or a list of primary keys as array() - * - * @return Returns affected rows or false on failure - */ - public function delete($where = null) - { - if (!empty($this->before_delete) || !empty($this->before_soft_delete) || !empty($this->after_delete) || !empty($this->after_soft_delete) || ($this->soft_deletes === true)) { - $to_update = array(); - if (isset($where)) { - $this->where($where); - } - $query = $this->_database->get($this->table); - foreach ($query->result() as $row) { - $to_update[] = array($this->primary_key => $row->{$this->primary_key}); - } - if (!empty($this->before_soft_delete)) { - foreach ($to_update as &$row) { - $row = $this->trigger('before_soft_delete', $row); - } - } - if (!empty($this->before_delete)) { - foreach ($to_update as &$row) { - $row = $this->trigger('before_delete', $row); - } - } - } - if (isset($where)) { - $this->where($where); - } - $affected_rows = 0; - if ($this->soft_deletes === true) { - if (isset($to_update) && count($to_update) > 0) { - foreach ($to_update as &$row) { - //$row = $this->trigger('before_soft_delete',$row); - $row[$this->_deleted_at_field] = $this->_the_timestamp(); - } - $affected_rows = $this->_database->update_batch($this->table, $to_update, $this->primary_key); - $to_update['affected_rows'] = $affected_rows; - $this->_prep_after_write(); - $this->trigger('after_soft_delete', $to_update); - } - - return $affected_rows; - } else { - if ($this->_database->delete($this->table)) { - $affected_rows = $this->_database->affected_rows(); - if (!empty($this->after_delete)) { - $to_update['affected_rows'] = $affected_rows; - $to_update = $this->trigger('after_delete', $to_update); - $affected_rows = $to_update; - } - $this->_prep_after_write(); - - return $affected_rows; - } - } - - return false; - } - - /** - * public function force_delete($where = NULL) - * Forces the delete of a row if soft_deletes is enabled - * - * @param null $where - * - * @return bool - */ - public function force_delete($where = null) - { - if (isset($where)) { - $this->where($where); - } - if ($this->_database->delete($this->table)) { - $this->_prep_after_write(); - - return $this->_database->affected_rows(); - } - - return false; - } - - /** - * public function restore($where = NULL) - * "Un-deletes" a row - * - * @param null $where - * - * @return bool - */ - public function restore($where = null) - { - $this->with_trashed(); - if (isset($where)) { - $this->where($where); - } - if ($affected_rows = $this->_database->update($this->table, array($this->_deleted_at_field => null))) { - $this->_prep_after_write(); - - return $affected_rows; - } - - return false; - } - - /** - * public function trashed($where = NULL) - * Verifies if a record (row) is soft_deleted or not - * - * @param null $where - * - * @return bool - */ - public function trashed($where = null) - { - $this->only_trashed(); - if (isset($where)) { - $this->where($where); - } - $this->limit(1); - $query = $this->_database->get($this->table); - if ($query->num_rows() == 1) { - return true; - } - - return false; - } - - public function _get_joined($requested) - { - $this->_database->join( - $this->_relationships[$requested['request']]['foreign_table'], - $this->table . '.' . $this->_relationships[$requested['request']]['local_key'] . ' = ' . $this->_relationships[$requested['request']]['foreign_table'] . '.' . $this->_relationships[$requested['request']]['foreign_key'] - ); - $the_select = ''; - if (!empty($requested['parameters'])) { - if (array_key_exists('fields', $requested['parameters'])) { - $fields = explode(',', $requested['parameters']['fields']); - $sub_select = array(); - foreach ($fields as $field) { - $sub_select[] = ((strpos( - $field, - '.' - ) === false) ? '`' . $this->_relationships[$requested['request']]['foreign_table'] . '`.`' . trim( - $field - ) . '`' : trim($field)) . ' AS ' . $requested['request'] . '_' . trim($field); - } - $the_select = implode(',', $sub_select); - } else { - $the_select = $this->_relationships[$requested['request']]['foreign_table'] . '.*'; - } - } - $this->_database->select($the_select); - unset($this->_requested[$requested['request']]); - } - - /** - * public function get() - * Retrieves one row from table. - * - * @param null $where - * - * @return mixed - */ - public function get($where = null) - { - $data = $this->_get_from_cache(); - if (isset($data) && $data !== false) { - $this->_database->reset_query(); - if (isset($this->_cache)) { - unset($this->_cache); - } - - return $data; - } else { - $this->trigger('before_get'); - if ($this->_select) { - $this->_database->select($this->_select); - } - if (!empty($this->_requested)) { - foreach ($this->_requested as $requested) { - if (isset($requested['parameters']['join'])) { - $this->_get_joined($requested); - } else { - $this->_database->select($this->_relationships[$requested['request']]['local_key']); - } - } - } - if (isset($where)) { - $this->where($where); - } elseif ($this->soft_deletes === true) { - $this->_where_trashed(); - } - $this->limit(1); - $query = $this->_database->get($this->table); - $this->_reset_trashed(); - if ($query->num_rows() == 1) { - $row = $query->row_array(); - $row = $this->trigger('after_get', $row); - $row = $this->_prep_after_read(array($row), false); - $row = $row[0]; - $this->_write_to_cache($row); - - return $row; - } else { - return false; - } - } - } - - /** - * public function get_all() - * Retrieves rows from table. - * - * @param null $where - * - * @return mixed - */ - public function get_all($where = null) - { - $data = $this->_get_from_cache(); - if (isset($data) && $data !== false) { - $this->_database->reset_query(); - if (isset($this->_cache)) { - unset($this->_cache); - } - - return $data; - } else { - $this->trigger('before_get'); - if (isset($where)) { - $this->where($where); - } elseif ($this->soft_deletes === true) { - $this->_where_trashed(); - } - if (isset($this->_select)) { - $this->_database->select($this->_select); - } - if (!empty($this->_requested)) { - foreach ($this->_requested as $requested) { - if (isset($requested['parameters']['join'])) { - $this->_get_joined($requested); - } else { - $this->_database->select($this->_relationships[$requested['request']]['local_key']); - } - } - } - $query = $this->_database->get($this->table); - $this->_reset_trashed(); - if ($query->num_rows() > 0) { - $data = $query->result_array(); - $data = $this->trigger('after_get', $data); - $data = $this->_prep_after_read($data, true); - $this->_write_to_cache($data); - - return $data; - } else { - return false; - } - } - } - - /** - * public function count_rows() - * Retrieves number of rows from table. - * - * @param null $where - * - * @return integer - */ - public function count_rows($where = null) - { - if (isset($where)) { - $this->where($where); - } elseif ($this->soft_deletes === true) { - $this->_where_trashed(); - } - $this->_database->from($this->table); - $number_rows = $this->_database->count_all_results(); - $this->_reset_trashed(); - - return $number_rows; - } - /** RELATIONSHIPS */ - /** - * public function with($requests) - * allows the user to retrieve records from other interconnected tables depending on the relations defined before - * the constructor - * - * @param string $request - * @param array $arguments - * - * @return $this - */ - public function with($request, $arguments = array()) - { - $this->_set_relationships(); - if (array_key_exists($request, $this->_relationships)) { - $this->_requested[$request] = array('request' => $request); - $parameters = array(); - if (isset($arguments)) { - foreach ($arguments as $argument) { - if (is_array($argument)) { - foreach ($argument as $k => $v) { - $parameters[$k] = $v; - } - } else { - $requested_operations = explode('|', $argument); - foreach ($requested_operations as $operation) { - $elements = explode(':', $operation, 2); - if (sizeof($elements) == 2) { - $parameters[$elements[0]] = $elements[1]; - } else { - show_error( - 'MY_Model: Parameters for with_*() method must be of the form: "...->with_*(\'where:...|fields:...\')"' - ); - } - } - } - } - } - $this->_requested[$request]['parameters'] = $parameters; - } - - /* - if($separate_subqueries === FALSE) - { - $this->separate_subqueries = FALSE; - foreach($this->_requested as $request) - { - if($this->_relationships[$request]['relation'] == 'has_one') $this->_has_one($request); - } - } - else - { - $this->after_get[] = 'join_temporary_results'; - } - */ - - return $this; - } - - /** - * protected function join_temporary_results($data) - * Joins the subquery results to the main $data - * - * @param $data - * - * @return mixed - */ - protected function join_temporary_results($data) - { - $order_by = array(); - $order_inside_array = array(); - //$order_inside = ''; - foreach ($this->_requested as $requested_key => $request) { - $pivot_table = null; - $relation = $this->_relationships[$request['request']]; - $this->load->model($relation['foreign_model'], $relation['foreign_model_name']); - $foreign_key = $relation['foreign_key']; - $local_key = $relation['local_key']; - $foreign_table = $relation['foreign_table']; - $type = $relation['relation']; - $relation_key = $relation['relation_key']; - if ($type == 'has_many_pivot') { - $pivot_table = $relation['pivot_table']; - $pivot_local_key = $relation['pivot_local_key']; - $pivot_foreign_key = $relation['pivot_foreign_key']; - $get_relate = $relation['get_relate']; - } - if (array_key_exists('order_inside', $request['parameters'])) { - //$order_inside = $request['parameters']['order_inside']; - $elements = explode(',', $request['parameters']['order_inside']); - foreach ($elements as $element) { - $order = explode(' ', $element); - if (sizeof($order) == 2) { - $order_inside_array[] = array( - trim($order[0]), - trim($order[1]) - ); - } else { - $order_inside_array[] = array( - trim($order[0]), - 'desc' - ); - } - } - } - $local_key_values = array(); - foreach ($data as $key => $element) { - if (isset($element[$local_key]) and !empty($element[$local_key])) { - $id = $element[$local_key]; - $local_key_values[$key] = $id; - } - } - if (!$local_key_values) { - $data[$key][$relation_key] = null; - continue; - } - if (!isset($pivot_table)) { - $sub_results = $this->{$relation['foreign_model_name']}; - $select = array(); - $select[] = '`' . $foreign_table . '`.`' . $foreign_key . '`'; - if (!empty($request['parameters'])) { - if (array_key_exists('fields', $request['parameters'])) { - if ($request['parameters']['fields'] == '*count*') { - $the_select = '*count*'; - $sub_results = (isset($the_select)) ? $sub_results->fields($the_select) : $sub_results; - $sub_results = $sub_results->fields($foreign_key); - } else { - $fields = explode(',', $request['parameters']['fields']); - foreach ($fields as $field) { - $select[] = (strpos($field, '.') === false) ? '`' . $foreign_table . '`.`' . trim( - $field - ) . '`' : trim($field); - } - $the_select = implode(',', $select); - $sub_results = (isset($the_select)) ? $sub_results->fields($the_select) : $sub_results; - } - } - if (array_key_exists( - 'fields', - $request['parameters'] - ) && ($request['parameters']['fields'] == '*count*')) { - $sub_results->group_by('`' . $foreign_table . '`.`' . $foreign_key . '`'); - } - if (array_key_exists('where', $request['parameters']) || array_key_exists( - 'non_exclusive_where', - $request['parameters'] - )) { - $the_where = array_key_exists( - 'where', - $request['parameters'] - ) ? 'where' : 'non_exclusive_where'; - } - $sub_results = isset($the_where) ? $sub_results->where( - $request['parameters'][$the_where], - null, - null, - false, - false, - true - ) : $sub_results; - if (isset($order_inside_array)) { - foreach ($order_inside_array as $order_by_inside) { - $sub_results = $sub_results->order_by($order_by_inside[0], $order_by_inside[1]); - } - } - //Add nested relation - if (array_key_exists('with', $request['parameters'])) { - // Do we have many nested relation - if (is_array( - $request['parameters']['with'] - ) && isset($request['parameters']['with'][0]) && is_array( - $request['parameters']['with'][0] - )) { - foreach ($request['parameters']['with'] as $with) { - $with_relation = array_shift($with); - $sub_results->with($with_relation, array($with)); - } - } else // single nested relation - { - $with_relation = array_shift($request['parameters']['with']); - $sub_results->with($with_relation, array($request['parameters']['with'])); - } - } - } - $sub_results = $sub_results->where($foreign_key, $local_key_values)->get_all(); - } else { - $this->_database->join( - $pivot_table, - $foreign_table . '.' . $foreign_key . ' = ' . $pivot_table . '.' . $pivot_foreign_key, - 'left' - ); - $this->_database->join( - $this->table, - $pivot_table . '.' . $pivot_local_key . ' = ' . $this->table . '.' . $local_key, - 'left' - ); - $this->_database->select($foreign_table . '.' . $foreign_key); - $this->_database->select($pivot_table . '.' . $pivot_local_key); - if (!empty($request['parameters'])) { - if (array_key_exists('fields', $request['parameters'])) { - if ($request['parameters']['fields'] == '*count*') { - $this->_database->select( - 'COUNT(`' . $foreign_table . '`.`' . $foreign_key . '`) as counted_rows, `' . $foreign_table . '`.`' . $foreign_key . '`', - false - ); - } else { - $fields = explode(',', $request['parameters']['fields']); - $select = array(); - foreach ($fields as $field) { - $select[] = (strpos($field, '.') === false) ? '`' . $foreign_table . '`.`' . trim( - $field - ) . '`' : trim($field); - } - $the_select = implode(',', $select); - $this->_database->select($the_select); - } - } - if (array_key_exists('where', $request['parameters']) || array_key_exists( - 'non_exclusive_where', - $request['parameters'] - )) { - $the_where = array_key_exists( - 'where', - $request['parameters'] - ) ? 'where' : 'non_exclusive_where'; - $this->_database->where($request['parameters'][$the_where], null, null, false, false, true); - } - } - $this->_database->where_in($pivot_table . '.' . $pivot_local_key, $local_key_values); - if (!empty($order_inside_array)) { - $order_inside_str = ''; - foreach ($order_inside_array as $order_by_inside) { - $order_inside_str .= (strpos( - $order_by_inside[0], - ',' - ) === false) ? '`' . $foreign_table . '`.`' . $order_by_inside[0] . ' ' . $order_by_inside[1] : $order_by_inside[0] . ' ' . $order_by_inside[1]; - $order_inside_str .= ','; - } - $order_inside_str = rtrim($order_inside_str, ","); - $this->_database->order_by(rtrim($order_inside_str, ",")); - } - $sub_results = $this->_database->get($foreign_table)->result_array(); - $this->_database->reset_query(); - } - if (isset($sub_results) && !empty($sub_results)) { - $subs = array(); - foreach ($sub_results as $result) { - $result_array = (array)$result; - $the_foreign_key = $result_array[$foreign_key]; - if (isset($pivot_table)) { - $the_local_key = $result_array[$pivot_local_key]; - if (isset($get_relate) and $get_relate === true) { - $subs[$the_local_key][$the_foreign_key] = $this->{$relation['foreign_model']}->where( - $foreign_key, - $result[$foreign_key] - )->get(); - } else { - $subs[$the_local_key][$the_foreign_key] = $result; - } - } else { - if ($type == 'has_one') { - $subs[$the_foreign_key] = $result; - } else { - $subs[$the_foreign_key][] = $result; - } - } - } - $sub_results = $subs; - foreach ($local_key_values as $key => $value) { - if (array_key_exists($value, $sub_results)) { - $data[$key][$relation_key] = $sub_results[$value]; - } else { - if (array_key_exists('where', $request['parameters'])) { - unset($data[$key]); - } - } - } - } else { - $data[$key][$relation_key] = null; - } - if (array_key_exists('order_by', $request['parameters'])) { - $elements = explode(',', $request['parameters']['order_by']); - if (sizeof($elements) == 2) { - $order_by[$relation_key] = array( - trim($elements[0]), - trim($elements[1]) - ); - } else { - $order_by[$relation_key] = array( - trim($elements[0]), - 'desc' - ); - } - } - unset($this->_requested[$requested_key]); - } - if (!empty($order_by)) { - foreach ($order_by as $field => $row) { - list($key, $value) = $row; - $data = $this->_build_sorter($data, $field, $key, $value); - } - } - - return $data; - } - - /** - * private function _has_one($request) - * - * returns a joining of two tables depending on the $request relationship established in the constructor - * - * @param $request - * - * @return $this - */ - private function _has_one($request) - { - $relation = $this->_relationships[$request]; - $this->_database->join( - $relation['foreign_table'], - $relation['foreign_table'] . '.' . $relation['foreign_key'] . ' = ' . $this->table . '.' . $relation['local_key'], - 'left' - ); - - return true; - } - - /** - * private function _set_relationships() - * - * Called by the public method with() it will set the relationships between the current model and other models - */ - private function _set_relationships() - { - if (empty($this->_relationships)) { - $options = array( - 'has_one', - 'has_many', - 'has_many_pivot' - ); - foreach ($options as $option) { - if (isset($this->{$option}) && !empty($this->{$option})) { - foreach ($this->{$option} as $key => $relation) { - $single_query = false; - if (!is_array($relation)) { - $foreign_model = $relation; - $model = $this->_parse_model_dir($foreign_model); - $foreign_model = $model['foreign_model']; - //$model_dir = $model['model_dir']; - $foreign_model_name = $model['foreign_model_name']; - $this->load->model($foreign_model, $foreign_model_name); - $foreign_table = $this->{$foreign_model_name}->table; - $foreign_key = $this->{$foreign_model_name}->primary_key; - $local_key = $this->primary_key; - $pivot_local_key = $this->table . '_' . $local_key; - $pivot_foreign_key = $foreign_table . '_' . $foreign_key; - $get_relate = false; - } else { - if ($this->is_assoc($relation)) { - $foreign_model = $relation['foreign_model']; - $model = $this->_parse_model_dir($foreign_model); - $foreign_model = $model['model_dir'] . $model['foreign_model']; - $foreign_model_name = $model['foreign_model_name']; - if (array_key_exists('foreign_table', $relation)) { - $foreign_table = $relation['foreign_table']; - } else { - $this->load->model($foreign_model, $foreign_model_name); - $foreign_table = $this->{$foreign_model_name}->table; - } - $foreign_key = $relation['foreign_key']; - $local_key = $relation['local_key']; - if ($option == 'has_many_pivot') { - $pivot_table = $relation['pivot_table']; - $pivot_local_key = (array_key_exists( - 'pivot_local_key', - $relation - )) ? $relation['pivot_local_key'] : $this->table . '_' . $this->primary_key; - $pivot_foreign_key = (array_key_exists( - 'pivot_foreign_key', - $relation - )) ? $relation['pivot_foreign_key'] : $foreign_table . '_' . $foreign_key; - $get_relate = (array_key_exists( - 'get_relate', - $relation - ) && ($relation['get_relate'] === true)) ? true : false; - } - if ($option == 'has_one' && isset($relation['join']) && $relation['join'] === true) { - $single_query = true; - } - } else { - $foreign_model = $relation[0]; - $model = $this->_parse_model_dir($foreign_model); - $foreign_model = $model['model_dir'] . $model['foreign_model']; - $foreign_model_name = $model['foreign_model_name']; - $this->load->model($foreign_model); - $foreign_table = $this->{$foreign_model}->table; - $foreign_key = $relation[1]; - $local_key = $relation[2]; - if ($option == 'has_many_pivot') { - $pivot_local_key = $this->table . '_' . $this->primary_key; - $pivot_foreign_key = $foreign_table . '_' . $foreign_key; - $get_relate = (isset($relation[3]) && ($relation[3] === TRUE())) ? true : false; - } - } - } - if ($option == 'has_many_pivot' && !isset($pivot_table)) { - $tables = array( - $this->table, - $foreign_table - ); - sort($tables); - $pivot_table = $tables[0] . '_' . $tables[1]; - } - $this->_relationships[$key] = array( - 'relation' => $option, - 'relation_key' => $key, - 'foreign_model' => bear_str_to_lower($foreign_model), - 'foreign_model_name' => bear_str_to_lower($foreign_model_name), - 'foreign_table' => $foreign_table, - 'foreign_key' => $foreign_key, - 'local_key' => $local_key - ); - if ($option == 'has_many_pivot') { - $this->_relationships[$key]['pivot_table'] = $pivot_table; - $this->_relationships[$key]['pivot_local_key'] = $pivot_local_key; - $this->_relationships[$key]['pivot_foreign_key'] = $pivot_foreign_key; - $this->_relationships[$key]['get_relate'] = $get_relate; - } - if ($single_query === true) { - $this->_relationships[$key]['joined'] = true; - } - } - } - } - } - } - /** END RELATIONSHIPS */ - /** - * public function on($connection_group = NULL) - * Sets a different connection to use for a query - * - * @param $connection_group = NULL - connection group in database setup - * - * @return obj - */ - public function on($connection_group = null) - { - if (isset($connection_group)) { - $this->_database->close(); - $this->load->database($connection_group); - $this->_database = $this->db; - } - - return $this; - } - - /** - * public function reset_connection($connection_group = NULL) - * Resets the connection to the default used for all the model - * - * @return obj - */ - public function reset_connection() - { - if (isset($connection_group)) { - $this->_database->close(); - $this->_set_connection(); - } - - return $this; - } - - /** - * Trigger an event and call its observers. Pass through the event name - * (which looks for an instance variable $this->event_name), an array of - * parameters to pass through and an optional 'last in interation' boolean - */ - public function trigger($event, $data = array(), $last = true) - { - if (isset($this->$event) && is_array($this->$event)) { - foreach ($this->$event as $method) { - if (strpos($method, '(')) { - preg_match('/([a-zA-Z0-9\_\-]+)(\(([a-zA-Z0-9\_\-\., ]+)\))?/', $method, $matches); - $method = $matches[1]; - $this->callback_parameters = explode(',', $matches[3]); - } - $data = call_user_func_array(array( - $this, - $method - ), array( - $data, - $last - )); - } - } - - return $data; - } - - /** - * private function _reset_trashed() - * Sets $_trashed to default 'without' - */ - private function _reset_trashed() - { - $this->_trashed = 'without'; - - return $this; - } - - /** - * public function with_trashed() - * Sets $_trashed to with - */ - public function with_trashed() - { - $this->_trashed = 'with'; - - return $this; - } - - /** - * public function without_trashed() - * Sets $_trashed to without - */ - public function without_trashed() - { - $this->_trashed = 'without'; - - return $this; - } - - /** - * public function with_trashed() - * Sets $_trashed to only - */ - public function only_trashed() - { - $this->_trashed = 'only'; - - return $this; - } - - private function _where_trashed() - { - switch ($this->_trashed) { - case 'only': - $this->_database->where( - $this->table . '.' . $this->_deleted_at_field . ' IS NOT NULL', - null, - false - ); - break; - case 'without': - $this->_database->where($this->table . '.' . $this->_deleted_at_field . ' IS NULL', null, false); - break; - case 'with': - break; - } - - //$this->_trashed = ''; issue #208... - return $this; - } - - /** - * public function fields($fields) - * does a select() of the $fields - * - * @param $fields the fields needed - * - * @return $this - */ - public function fields($fields = null) - { - if (isset($fields)) { - if ($fields == '*count*') { - $this->_select = ''; - $this->_database->select('COUNT(*) AS counted_rows', false); - } else { - $this->_select = array(); - $fields = (!is_array($fields)) ? explode(',', $fields) : $fields; - if (!empty($fields)) { - foreach ($fields as &$field) { - $exploded = explode('.', $field); - if (sizeof($exploded) < 2) { - $field = $this->table . '.' . $field; - } - } - } - $this->_select = $fields; - } - } else { - $this->_select = null; - } - - return $this; - } - - /** - * public function order_by($criteria, $order = 'ASC' - * A wrapper to $this->_database->order_by() - * - * @param $criteria - * @param string $order - * - * @return $this - */ - public function order_by($criteria, $order = 'ASC') - { - if (is_array($criteria)) { - foreach ($criteria as $key => $value) { - $this->_database->order_by($key, $value); - } - } else { - $this->_database->order_by($criteria, $order); - } - - return $this; - } - - /** - * Return the next call as an array rather than an object - */ - public function as_array() - { - $this->return_as = 'array'; - - return $this; - } - - /** - * Return the next call as an object rather than an array - */ - public function as_object() - { - $this->return_as = 'object'; - - return $this; - } - - public function as_dropdown($field = null) - { - if (!isset($field)) { - show_error( - 'MY_Model: You must set a field to be set as value for the key: ...->as_dropdown(\'field\')->...' - ); - exit; - } - $this->return_as_dropdown = 'dropdown'; - $this->_dropdown_field = $field; - $this->_select = array( - $this->primary_key, - $field - ); - - return $this; - } - - protected function _get_from_cache($cache_name = null) - { - if (isset($cache_name) || (isset($this->_cache) && !empty($this->_cache))) { - $this->load->driver('cache'); - $cache_name = isset($cache_name) ? $cache_name : $this->_cache['cache_name']; - $data = $this->cache->{$this->cache_driver}->get($cache_name); - - return $data; - } - } - - protected function _write_to_cache($data, $cache_name = null) - { - if (isset($cache_name) || (isset($this->_cache) && !empty($this->_cache))) { - $this->load->driver('cache'); - $cache_name = isset($cache_name) ? $cache_name : $this->_cache['cache_name']; - $seconds = $this->_cache['seconds']; - if (isset($cache_name) && isset($seconds)) { - $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); - $this->_reset_cache($cache_name); - - return true; - } - - return false; - } - } - - public function set_cache($string, $seconds = 86400) - { - $prefix = (bear_str_length($this->cache_prefix) > 0) ? $this->cache_prefix . '_' : ''; - $prefix .= $this->table . '_'; - $this->_cache = array( - 'cache_name' => $prefix . $string, - 'seconds' => $seconds - ); - - return $this; - } - - private function _reset_cache($string) - { - if (isset($string)) { - $this->_cache = array(); - } - - return $this; - } - - public function delete_cache($string = null) - { - $this->load->driver('cache'); - $prefix = (bear_str_length($this->cache_prefix) > 0) ? $this->cache_prefix . '_' : ''; - $prefix .= $this->table . '_'; - if (isset($string) && (strpos($string, '*') === false)) { - $this->cache->{$this->cache_driver}->delete($prefix . $string); - } else { - $cached = $this->cache->file->cache_info(); - foreach ($cached as $file) { - if (array_key_exists('relative_path', $file)) { - $path = $file['relative_path']; - break; - } - } - $mask = (isset($string)) ? $path . $prefix . $string : $path . $this->cache_prefix . '_*'; - array_map('unlink', glob($mask)); - } - - return $this; - } - - /** - * private function _set_timestamps() - * - * Sets the fields for the created_at, updated_at and deleted_at timestamps - * - * @return bool - */ - private function _set_timestamps() - { - if ($this->timestamps !== false) { - $this->_created_at_field = (is_array( - $this->timestamps - ) && isset($this->timestamps[0])) ? $this->timestamps[0] : 'created_at'; - $this->_updated_at_field = (is_array( - $this->timestamps - ) && isset($this->timestamps[1])) ? $this->timestamps[1] : 'updated_at'; - $this->_deleted_at_field = (is_array( - $this->timestamps - ) && isset($this->timestamps[2])) ? $this->timestamps[2] : 'deleted_at'; - } - - return true; - } - - /** - * private function _the_timestamp() - * - * returns a value representing the date/time depending on the timestamp format choosed - * - * @return string - */ - private function _the_timestamp() - { - if ($this->timestamps_format == 'timestamp') { - return time(); - } else { - return date($this->timestamps_format); - } - } - - /** - * private function _set_connection() - * - * Sets the connection to database - */ - private function _set_connection() - { - if (isset($this->_database_connection)) { - $this->_database = $this->load->database($this->_database_connection, true); - } else { - $this->load->database(); - $this->_database = $this->db; - } - - // This may not be required - return $this; - } - - /* - * HELPER FUNCTIONS - */ - public function paginate($rows_per_page, $total_rows = null, $page_number = 1) - { - $this->load->helper('url'); - $segments = $this->uri->total_segments(); - $uri_array = $this->uri->segment_array(); - $page = $this->uri->segment($segments); - if (is_numeric($page)) { - $page_number = $page; - } else { - $page_number = $page_number; - $uri_array[] = $page_number; - ++$segments; - } - $next_page = $page_number + 1; - $previous_page = $page_number - 1; - if ($page_number == 1) { - $this->previous_page = $this->pagination_delimiters[0] . $this->pagination_arrows[0] . $this->pagination_delimiters[1]; - } else { - $uri_array[$segments] = $previous_page; - $uri_string = implode('/', $uri_array); - $this->previous_page = $this->pagination_delimiters[0] . anchor( - $uri_string, - $this->pagination_arrows[0] - ) . $this->pagination_delimiters[1]; - } - $uri_array[$segments] = $next_page; - $uri_string = implode('/', $uri_array); - if (isset($total_rows) && (ceil($total_rows / $rows_per_page) == $page_number)) { - $this->next_page = $this->pagination_delimiters[0] . $this->pagination_arrows[1] . $this->pagination_delimiters[1]; - } else { - $this->next_page = $this->pagination_delimiters[0] . anchor( - $uri_string, - $this->pagination_arrows[1] - ) . $this->pagination_delimiters[1]; - } - $rows_per_page = (is_numeric($rows_per_page)) ? $rows_per_page : 10; - if (isset($total_rows)) { - if ($total_rows != 0) { - $number_of_pages = ceil($total_rows / $rows_per_page); - $links = $this->previous_page; - for ($i = 1; $i <= $number_of_pages; $i++) { - unset($uri_array[$segments]); - $uri_string = implode('/', $uri_array); - $links .= $this->pagination_delimiters[0]; - $links .= (($page_number == $i) ? anchor($uri_string, $i) : anchor($uri_string . '/' . $i, $i)); - $links .= $this->pagination_delimiters[1]; - } - $links .= $this->next_page; - $this->all_pages = $links; - } else { - $this->all_pages = $this->pagination_delimiters[0] . $this->pagination_delimiters[1]; - } - } - if (isset($this->_cache) && !empty($this->_cache)) { - $this->load->driver('cache'); - $cache_name = $this->_cache['cache_name'] . '_' . $page_number; - $seconds = $this->_cache['seconds']; - $data = $this->cache->{$this->cache_driver}->get($cache_name); - } - if (isset($data) && $data !== false) { - return $data; - } else { - $this->trigger('before_get'); - $this->where(); - $this->limit($rows_per_page, (($page_number - 1) * $rows_per_page)); - $data = $this->get_all(); - if ($data) { - if (isset($cache_name) && isset($seconds)) { - $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); - $this->_reset_cache($cache_name); - } - - return $data; - } else { - return false; - } - } - } - - public function set_pagination_delimiters($delimiters) - { - if (is_array($delimiters) && sizeof($delimiters) == 2) { - $this->pagination_delimiters = $delimiters; - } - - return $this; - } - - public function set_pagination_arrows($arrows) - { - if (is_array($arrows) && sizeof($arrows) == 2) { - $this->pagination_arrows = $arrows; - } - - return $this; - } - - /** - * private function _fetch_table() - * - * Sets the table name when called by the constructor - * - */ - private function _fetch_table() - { - if (!isset($this->table)) { - $this->table = $this->_get_table_name(get_class($this)); - } - - return true; - } - - private function _get_table_name($model_name) - { - $model_name = bear_str_to_lower($model_name); - $table_name = plural(preg_replace('/(_m|_model|_mdl)?$/', '', $model_name)); - - return $table_name; - } - - public function __call($method, $arguments) - { - if (substr($method, 0, 6) == 'where_') { - $column = substr($method, 6); - $this->where($column, $arguments); - - return $this; - } - if (($method != 'with_trashed') && (substr($method, 0, 5) == 'with_')) { - $relation = substr($method, 5); - $this->with($relation, $arguments); - - return $this; - } - if (method_exists($this->_database, $method)) { - call_user_func_array(array( - $this->_database, - $method - ), $arguments); - - return $this; - } - $parent_class = get_parent_class($this); - if ($parent_class !== false && !method_exists($parent_class, $method) && !method_exists($this, $method)) { - $msg = 'The method "' . $method . '" does not exist in ' . get_class( - $this - ) . ' or MY_Model or CI_Model.'; - show_error($msg, EXIT_UNKNOWN_METHOD, 'Method Not Found'); - } - } - - private function _build_sorter($data, $field, $order_by, $sort_by = 'DESC') - { - usort($data, function ($a, $b) use ($field, $order_by, $sort_by) { - $array_a = isset($a[$field]) ? $this->object_to_array($a[$field]) : null; - $array_b = isset($b[$field]) ? $this->object_to_array($b[$field]) : null; - - return strtoupper( - $sort_by - ) == "DESC" ? ((isset($array_a[$order_by]) && isset($array_b[$order_by])) ? ($array_a[$order_by] < $array_b[$order_by]) : (!isset($array_a) ? 1 : -1)) : ((isset($array_a[$order_by]) && isset($array_b[$order_by])) ? ($array_a[$order_by] > $array_b[$order_by]) : (!isset($array_b) ? 1 : -1)); - }); - - return $data; - } - - public function object_to_array($object) - { - if (!is_object($object) && !is_array($object)) { - return $object; - } - if (is_object($object)) { - $object = get_object_vars($object); - } - - return array_map(array( - $this, - 'object_to_array' - ), $object); - } - - /** - * Verifies if an array is associative or not - * - * @param array $array - * - * @return bool - */ - protected function is_assoc(array $array) - { - return (bool)count(array_filter(array_keys($array), 'is_string')); - } - - /** - * private function _parse_model_dir($foreign_model) - * - * Parse model and model folder - * - * @param $foreign_model - * - * @return $data - */ - private function _parse_model_dir($foreign_model) - { - $data['foreign_model'] = $foreign_model; - $data['model_dir'] = ''; - $full_model = explode('/', $data['foreign_model']); - if ($full_model) { - $data['foreign_model'] = end($full_model); - $data['model_dir'] = str_replace($data['foreign_model'], null, implode('/', $full_model)); - } - $foreign_model_name = str_replace('/', '_', $data['model_dir'] . $data['foreign_model']); - $foreign_model_name = bear_str_to_lower($foreign_model_name); - - $data['foreign_model_name'] = $foreign_model_name; - - return $data; - } - /* - public function add_creator($data) - { - $data['created_by'] = $_SESSION['user_id']; - return $data; - } - */ - /* - public function add_updater($data) - { - $data['updated_by'] = $_SESSION['user_id']; - return $data; - } - */ - } + /** + * Created by PhpStorm. + * User: hungna + * Date: 3/16/2017 + * Time: 1:59 PM + */ + /* + * Copyright (C) 2014 @avenirer [avenir.ro@gmail.com] + * Everyone is permitted to copy and distribute verbatim or modified copies of this license document, + * and changing it is allowed as long as the name is changed. + * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + * + ***** Do whatever you like with the original work, just don't be a dick. + ***** Being a dick includes - but is not limited to - the following instances: + ********* 1a. Outright copyright infringement - Don't just copy this and change the name. + ********* 1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick. + ********* 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick. + ***** If you become rich through modifications, related works/services, or supporting the original work, share the love. Only a dick would make loads off this work and not buy the original works creator(s) a pint. + ***** Code is provided with no warranty. + *********** Using somebody else's code and bitching when it goes wrong makes you a DONKEY dick. + *********** Fix the problem yourself. A non-dick would submit the fix back. + * + */ + + /** how to extend MY_Model: + * class User_model extends MY_Model + * { + * public $table = 'users'; // Set the name of the table for this model. + * public $primary_key = 'id'; // Set the primary key + * public $fillable = array(); // You can set an array with the fields that can be filled by insert/update + * public $protected = array(); // ...Or you can set an array with the fields that cannot be filled by + * insert/update public function __construct() + * { + * $this->_database_connection = group_name or array() | OPTIONAL + * Sets the connection preferences (group name) set up in the database.php. If not trset, it will use the + * 'default' (the $active_group) database connection. + * $this->timestamps = TRUE | array('made_at','modified_at','removed_at') + * If set to TRUE tells MY_Model that the table has 'created_at','updated_at' (and 'deleted_at' if + * $this->soft_delete is set to TRUE) If given an array as parameter, it tells MY_Model, that the first + * element is a created_at field type, the second element is a updated_at field type (and the third + * element is a deleted_at field type) + * $this->soft_deletes = FALSE; + * Enables (TRUE) or disables (FALSE) the "soft delete" on records. Default is FALSE + * $this->timestamps_format = 'Y-m-d H:i:s' + * You can at any time change the way the timestamp is created (the default is the MySQL standard datetime + * format) by modifying this variable. You can choose between whatever format is acceptable by the php + * function date() (default is 'Y-m-d H:i:s'), or 'timestamp' (UNIX timestamp) + * $this->return_as = 'object' | 'array' + * Allows the model to return the results as object or as array + * $this->has_one['phone'] = 'Phone_model' or $this->has_one['phone'] = + * array('Phone_model','foreign_key','local_key'); + * $this->has_one['address'] = 'Address_model' or $this->has_one['address'] = + * array('Address_model','foreign_key','another_local_key'); Allows establishing ONE TO ONE or more ONE TO ONE + * relationship(s) between models/tables + * $this->has_many['posts'] = 'Post_model' or $this->has_many['posts'] = + * array('Posts_model','foreign_key','another_local_key'); Allows establishing ONE TO MANY or more ONE TO MANY + * relationship(s) between models/tables + * $this->has_many_pivot['posts'] = 'Post_model' or $this->has_many_pivot['posts'] = + * array('Posts_model','foreign_primary_key','local_primary_key'); Allows establishing MANY TO MANY or more + * MANY TO MANY relationship(s) between models/tables with the use of a PIVOT TABLE + * !ATTENTION: The pivot table name must be composed of the two table names separated by "_" the table + * names having to to be alphabetically ordered (NOT users_posts, but posts_users). Also the pivot table + * must contain as identifying columns the columns named by convention as follows: table_name_singular + _ + * + foreign_table_primary_key. For example: considering that a post can have multiple authors, a pivot + * table that connects two tables (users and posts) must be named posts_users and must have post_id and + * user_id as identifying columns for the posts.id and users.id tables. + * $this->cache_driver = 'file' + * $this->cache_prefix = 'mm' + * If you know you will do some caching of results without the native caching solution, you can at any + * time use the MY_Model's caching. By default, MY_Model uses the files to cache result. If you want to + * change the way it stores the cache, you can change the $cache_driver property to whatever CodeIgniter + * cache driver you want to use. Also, with $cache_prefix, you can prefix the name of the caches. by + * default any cache made by MY_Model starts with 'mm' + _ + "name chosen for cache" + * $this->delete_cache_on_save = FALSE + * If you use caching often and you don't want to be forced to delete cache manually, you can enable + * $this->delete_cache_on_save by setting it to TRUE. If set to TRUE the model will auto-delete all cache + * related to the model's table whenever you write/update/delete data from that table. + * $this->pagination_delimiters = array('',''); + * If you know you will use the paginate() method, you can change the delimiters between the pages links + * $this->pagination_arrows = array('<','>'); + * You can also change the way the previous and next arrows look like. + * + * + * parent::__construct(); + * } + * } + * + * @property \CI_Loader load + * @property \CI_Form_validation form_validation + **/ + class HungNG_Model extends CI_Model + { + /** + * Select the database connection from the group names defined inside the database.php configuration file or an + * array. + */ + protected $_database_connection = null; + /** @var + * This one will hold the database connection object + */ + protected $_database; + /** @var null + * Sets table name + */ + public $table = null; + /** + * @var null + * Sets PRIMARY KEY + */ + public $primary_key = 'id'; + /** + * @var array + * You can establish the fields of the table. If you won't these fields will be filled by MY_Model (with one query) + */ + public $table_fields = array(); + /** + * @var array + * Sets fillable fields + */ + public $fillable = array(); + /** + * @var array + * Sets protected fields + */ + public $protected = array(); + private $_can_be_filled = null; + /** @var bool | array + * Enables created_at and updated_at fields + */ + protected $timestamps = true; + protected $timestamps_format = 'Y-m-d H:i:s'; + protected $_created_at_field; + protected $_updated_at_field; + protected $_deleted_at_field; + /** @var bool + * Enables soft_deletes + */ + protected $soft_deletes = false; + /** relationships variables */ + private $_relationships = array(); + public $has_one = array(); + public $has_many = array(); + public $has_many_pivot = array(); + public $separate_subqueries = true; + private $_requested = array(); + /** end relationships variables */ + /*caching*/ + public $cache_driver = 'file'; + public $cache_prefix = 'mm'; + protected $_cache = array(); + public $delete_cache_on_save = false; + /*pagination*/ + public $next_page; + public $previous_page; + public $all_pages; + public $pagination_delimiters; + public $pagination_arrows; + /* validation */ + private $validated = true; + private $row_fields_to_update = array(); + /** + * The various callbacks available to the model. Each are + * simple lists of method names (methods will be run on $this). + */ + protected $before_create = array(); + protected $after_create = array(); + protected $before_update = array(); + protected $after_update = array(); + protected $before_get = array(); + protected $after_get = array(); + protected $before_delete = array(); + protected $after_delete = array(); + protected $before_soft_delete = array(); + protected $after_soft_delete = array(); + protected $callback_parameters = array(); + protected $return_as = 'object'; + protected $return_as_dropdown = null; + protected $_dropdown_field = ''; + private $_trashed = 'without'; + private $_select = '*'; + + public function __construct() + { + parent::__construct(); + + log_message('info', 'HungNG_Model Class Initialized'); + + $this->load->helper('inflector'); + $this->_set_connection(); + $this->_set_timestamps(); + $this->_fetch_table(); + $this->pagination_delimiters = (isset($this->pagination_delimiters)) ? $this->pagination_delimiters : array( + '', + '' + ); + $this->pagination_arrows = (isset($this->pagination_arrows)) ? $this->pagination_arrows : array( + '<', + '>' + ); + /* These below are implementation examples for before_create and before_update triggers. + Their respective functions - add_creator() and add_updater() - can be found at the end of the model. + They add user id on create and update. If you comment this out don't forget to do the same for the methods() + $this->before_create[]='add_creator'; + $this->before_update[]='add_updater'; + */ + } + + public function __destruct() + { + if ($this->db->conn_id) { + $this->db->close(); + log_message( + 'info', + 'Class "HungNG_Model" Class Destructed and database disconnected' + ); + } + } + + public function _get_table_fields() + { + if (empty($this->table_fields)) { + $this->table_fields = $this->_database->list_fields($this->table); + } + + return true; + } + + public function fillable_fields() + { + if (!isset($this->_can_be_filled)) { + $this->_get_table_fields(); + $no_protection = array(); + foreach ($this->table_fields as $field) { + if (!in_array($field, $this->protected)) { + $no_protection[] = $field; + } + } + if (!empty($this->fillable)) { + $can_fill = array(); + foreach ($this->fillable as $field) { + if (in_array($field, $no_protection)) { + $can_fill[] = $field; + } + } + $this->_can_be_filled = $can_fill; + } else { + $this->_can_be_filled = $no_protection; + } + } + + return true; + } + + public function _prep_before_write($data) + { + $this->fillable_fields(); + // We make sure we have the fields that can be filled + $can_fill = $this->_can_be_filled; + // Let's make sure we receive an array... + $data_as_array = (is_object($data)) ? (array)$data : $data; + $new_data = array(); + $multi = $this->is_multidimensional($data); + if ($multi === false) { + foreach ($data_as_array as $field => $value) { + if (in_array($field, $can_fill)) { + $new_data[$field] = $value; + } else { + show_error('MY_Model: Unknown column (' . $field . ') in table: (' . $this->table . ').'); + } + } + } else { + foreach ($data_as_array as $key => $row) { + foreach ($row as $field => $value) { + if (in_array($field, $can_fill)) { + $new_data[$key][$field] = $value; + } else { + show_error('MY_Model: Unknown column ' . $field . ' in table: ' . $this->table); + } + } + } + } + + return $new_data; + } + + /* + * public function _prep_after_write() + * this function simply deletes the cache related to the model's table if $this->delete_cache_on_save is set to TRUE + * It should be called by any "save" method + */ + public function _prep_after_write() + { + if ($this->delete_cache_on_save === true) { + $this->delete_cache('*'); + } + + return true; + } + + public function _prep_before_read() + { + } + + public function _prep_after_read($data, $multi = true) + { + // let's join the subqueries... + $data = $this->join_temporary_results($data); + $this->_database->reset_query(); + $this->_requested = array(); + if (isset($this->return_as_dropdown) && $this->return_as_dropdown == 'dropdown') { + foreach ($data as $row) { + $dropdown[$row[$this->primary_key]] = $row[$this->_dropdown_field]; + } + $data = $dropdown; + $this->return_as_dropdown = null; + } elseif ($this->return_as == 'object') { + $data = json_decode(json_encode($data), false); + } + if (isset($this->_select)) { + $this->_select = '*'; + } + + return $data; + } + + /** + * public function from_form($rules = NULL,$additional_values = array(), $row_fields_to_update = array()) + * Gets data from form, after validating it and waits for an insert() or update() method in the query chain + * + * @param null $rules Gets the validation rules. If nothing is passed (NULL), will look for the + * validation rules inside the model $rules public property + * @param array $additional_values Accepts additional fields to be filled, fields that are not to be found + * inside + * the form. The values are inserted as an array with "field_name" => + * "field_value" + * @param array $row_fields_to_update You can mention the fields from the form that can be used to identify + * the row when doing an update + * + * @return $this + */ + public function from_form($rules = null, $additional_values = null, $row_fields_to_update = array()) + { + $this->_get_table_fields(); + $this->load->library('form_validation'); + if (!isset($rules)) { + if (empty($row_fields_to_update)) { + $rules = $this->rules['insert']; + } else { + $rules = $this->rules['update']; + } + } + $this->form_validation->set_rules($rules); + if ($this->form_validation->run()) { + $this->fillable_fields(); + $this->validated = array(); + foreach ($rules as $rule) { + if (in_array($rule['field'], $this->_can_be_filled)) { + $this->validated[$rule['field']] = $this->input->post($rule['field']); + } + } + if (isset($additional_values) && is_array($additional_values) && !empty($additional_values)) { + foreach ($additional_values as $field => $value) { + if (in_array($field, $this->_can_be_filled)) { + $this->validated[$field] = $value; + } + } + } + if (!empty($row_fields_to_update)) { + foreach ($row_fields_to_update as $key => $field) { + if (in_array($field, $this->table_fields)) { + $this->row_fields_to_update[$field] = $this->input->post($field); + } elseif (in_array($key, $this->table_fields)) { + $this->row_fields_to_update[$key] = $field; + } else { + continue; + } + } + } + + return $this; + } else { + $this->validated = false; + + return $this; + } + } + + /** + * public function insert($data) + * Inserts data into table. Can receive an array or a multidimensional array depending on what kind of insert we're + * talking about. + * + * @param $data + * + * @return int/array Returns id/ids of inserted rows + */ + public function insert($data = null) + { + if (!isset($data) && $this->validated != false) { + $data = $this->validated; + $this->validated = false; + } elseif (!isset($data)) { + return false; + } + $data = $this->_prep_before_write($data); + //now let's see if the array is a multidimensional one (multiple rows insert) + $multi = $this->is_multidimensional($data); + // if the array is not a multidimensional one... + if ($multi === false) { + if ($this->timestamps !== false) { + $data[$this->_created_at_field] = $this->_the_timestamp(); + } + $data = $this->trigger('before_create', $data); + if ($this->_database->insert($this->table, $data)) { + $this->_prep_after_write(); + $id = $this->_database->insert_id(); + $return = $this->trigger('after_create', $id); + + return $return; + } + + return false; + } // else... + else { + $return = array(); + foreach ($data as $row) { + if ($this->timestamps !== false) { + $row[$this->_created_at_field] = $this->_the_timestamp(); + } + $row = $this->trigger('before_create', $row); + if ($this->_database->insert($this->table, $row)) { + $return[] = $this->_database->insert_id(); + } + } + $this->_prep_after_write(); + $after_create = array(); + foreach ($return as $id) { + $after_create[] = $this->trigger('after_create', $id); + } + + return $after_create; + } + + return false; + } + + /* + * public function is_multidimensional($array) + * Verifies if an array is multidimensional or not; + * @param array $array + * @return bool return TRUE if the array is a multidimensional one + */ + public function is_multidimensional($array) + { + if (is_array($array)) { + foreach ($array as $element) { + if (is_array($element)) { + return true; + } + } + } + + return false; + } + + /** + * public function update($data) + * Updates data into table. Can receive an array or a multidimensional array depending on what kind of update we're + * talking about. + * + * @param array $data + * @param array|int $column_name_where + * @param bool $escape should the values be escaped or not - defaults to true + * + * @return str/array Returns id/ids of inserted rows + */ + public function update($data = null, $column_name_where = null, $escape = true) + { + if (!isset($data) && $this->validated != false) { + $data = $this->validated; + $this->validated = false; + } elseif (!isset($data)) { + $this->_database->reset_query(); + + return false; + } + // Prepare the data... + $data = $this->_prep_before_write($data); + //now let's see if the array is a multidimensional one (multiple rows insert) + $multi = $this->is_multidimensional($data); + // if the array is not a multidimensional one... + if ($multi === false) { + if ($this->timestamps !== false) { + $data[$this->_updated_at_field] = $this->_the_timestamp(); + } + $data = $this->trigger('before_update', $data); + if ($this->validated === false && count($this->row_fields_to_update)) { + $this->where($this->row_fields_to_update); + $this->row_fields_to_update = array(); + } + if (isset($column_name_where)) { + if (is_array($column_name_where)) { + $this->where($column_name_where); + } elseif (is_numeric($column_name_where)) { + $this->_database->where($this->primary_key, $column_name_where); + } else { + $column_value = (is_object($data)) ? $data->{$column_name_where} : $data[$column_name_where]; + $this->_database->where($column_name_where, $column_value); + } + } + if ($escape) { + if ($this->_database->update($this->table, $data)) { + $this->_prep_after_write(); + $affected = $this->_database->affected_rows(); + $return = $this->trigger('after_update', $affected); + + return $return; + } + } else { + if ($this->_database->set($data, null, false)->update($this->table)) { + $this->_prep_after_write(); + $affected = $this->_database->affected_rows(); + $return = $this->trigger('after_update', $affected); + + return $return; + } + } + + return false; + } // else... + else { + $rows = 0; + foreach ($data as $row) { + if ($this->timestamps !== false) { + $row[$this->_updated_at_field] = $this->_the_timestamp(); + } + $row = $this->trigger('before_update', $row); + if (is_array($column_name_where)) { + $this->_database->where($column_name_where[0], $column_name_where[1]); + } else { + $column_value = (is_object($row)) ? $row->{$column_name_where} : $row[$column_name_where]; + $this->_database->where($column_name_where, $column_value); + } + if ($escape) { + if ($this->_database->update($this->table, $row)) { + $rows++; + } + } else { + if ($this->_database->set($row, null, false)->update($this->table)) { + $rows++; + } + } + } + $affected = $rows; + $this->_prep_after_write(); + $return = $this->trigger('after_update', $affected); + + return $return; + } + + return false; + } + + /** + * public function where($field_or_array = NULL, $operator_or_value = NULL, $value = NULL, $with_or = FALSE, + * $with_not = FALSE, $custom_string = FALSE) Sets a where method for the $this object + * + * @param null $field_or_array - can receive a field name or an array with more wheres... + * @param null $operator_or_value - can receive a database operator or, if it has a field, the value to equal with + * @param null $value - a value if it received a field name and an operator + * @param bool $with_or - if set to true will create a or_where query type pr a or_like query type, + * depending on the operator + * @param bool $with_not - if set to true will also add "NOT" in the where + * @param bool $custom_string - if set to true, will simply assume that $field_or_array is actually a string + * and pass it to the where query + * + * @return $this + */ + public function where( + $field_or_array = null, + $operator_or_value = null, + $value = null, + $with_or = false, + $with_not = false, + $custom_string = false + ) { + if ($this->soft_deletes === true) { + $backtrace = debug_backtrace(); #fix for lower PHP 5.4 version + if ($backtrace[1]['function'] != 'force_delete') { + $this->_where_trashed(); + } + } + if (is_array($field_or_array)) { + $multi = $this->is_multidimensional($field_or_array); + if ($multi === true) { + foreach ($field_or_array as $where) { + $field = $where[0]; + $operator_or_value = isset($where[1]) ? $where[1] : null; + $value = isset($where[2]) ? $where[2] : null; + $with_or = (isset($where[3])) ? true : false; + $with_not = (isset($where[4])) ? true : false; + $this->where($field, $operator_or_value, $value, $with_or, $with_not); + } + + return $this; + } + } + if ($with_or === true) { + $where_or = 'or_where'; + } else { + $where_or = 'where'; + } + if ($with_not === true) { + $not = '_not'; + } else { + $not = ''; + } + if ($custom_string === true) { + $this->_database->{$where_or}($field_or_array, null, false); + } elseif (is_numeric($field_or_array)) { + $this->_database->{$where_or}(array($this->table . '.' . $this->primary_key => $field_or_array)); + } elseif (is_array($field_or_array) && !isset($operator_or_value)) { + $this->_database->where($field_or_array); + } elseif (!isset($value) && isset($field_or_array) && isset($operator_or_value) && !is_array( + $operator_or_value + )) { + $this->_database->{$where_or}(array($this->table . '.' . $field_or_array => $operator_or_value)); + } elseif (!isset($value) && isset($field_or_array) && isset($operator_or_value) && is_array( + $operator_or_value + ) && !is_array($field_or_array)) { + //echo $field_or_array; + //exit; + $this->_database->{$where_or . $not . '_in'}($this->table . '.' . $field_or_array, $operator_or_value); + } elseif (isset($field_or_array) && isset($operator_or_value) && isset($value)) { + $operator_or_value = bear_str_to_lower($operator_or_value); + if ($operator_or_value === 'like') { + if ($with_not === true) { + $like = 'not_like'; + } else { + $like = 'like'; + } + if ($with_or === true) { + $like = 'or_' . $like; + } + $this->_database->{$like}($field_or_array, $value); + } else { + $this->_database->{$where_or}($field_or_array . ' ' . $operator_or_value, $value); + } + } + + return $this; + } + + /** + * public function limit($limit, $offset = 0) + * Sets a rows limit to the query + * + * @param $limit + * @param int $offset + * + * @return $this + */ + public function limit($limit, $offset = 0) + { + $this->_database->limit($limit, $offset); + + return $this; + } + + /** + * public function group_by($grouping_by) + * A wrapper to $this->_database->group_by() + * + * @param $grouping_by + * + * @return $this + */ + public function group_by($grouping_by) + { + $this->_database->group_by($grouping_by); + + return $this; + } + + /** + * public function delete($where) + * Deletes data from table. + * + * @param $where primary_key(s) Can receive the primary key value or a list of primary keys as array() + * + * @return Returns affected rows or false on failure + */ + public function delete($where = null) + { + if (!empty($this->before_delete) || !empty($this->before_soft_delete) || !empty($this->after_delete) || !empty($this->after_soft_delete) || ($this->soft_deletes === true)) { + $to_update = array(); + if (isset($where)) { + $this->where($where); + } + $query = $this->_database->get($this->table); + foreach ($query->result() as $row) { + $to_update[] = array($this->primary_key => $row->{$this->primary_key}); + } + if (!empty($this->before_soft_delete)) { + foreach ($to_update as &$row) { + $row = $this->trigger('before_soft_delete', $row); + } + } + if (!empty($this->before_delete)) { + foreach ($to_update as &$row) { + $row = $this->trigger('before_delete', $row); + } + } + } + if (isset($where)) { + $this->where($where); + } + $affected_rows = 0; + if ($this->soft_deletes === true) { + if (isset($to_update) && count($to_update) > 0) { + foreach ($to_update as &$row) { + //$row = $this->trigger('before_soft_delete',$row); + $row[$this->_deleted_at_field] = $this->_the_timestamp(); + } + $affected_rows = $this->_database->update_batch($this->table, $to_update, $this->primary_key); + $to_update['affected_rows'] = $affected_rows; + $this->_prep_after_write(); + $this->trigger('after_soft_delete', $to_update); + } + + return $affected_rows; + } else { + if ($this->_database->delete($this->table)) { + $affected_rows = $this->_database->affected_rows(); + if (!empty($this->after_delete)) { + $to_update['affected_rows'] = $affected_rows; + $to_update = $this->trigger('after_delete', $to_update); + $affected_rows = $to_update; + } + $this->_prep_after_write(); + + return $affected_rows; + } + } + + return false; + } + + /** + * public function force_delete($where = NULL) + * Forces the delete of a row if soft_deletes is enabled + * + * @param null $where + * + * @return bool + */ + public function force_delete($where = null) + { + if (isset($where)) { + $this->where($where); + } + if ($this->_database->delete($this->table)) { + $this->_prep_after_write(); + + return $this->_database->affected_rows(); + } + + return false; + } + + /** + * public function restore($where = NULL) + * "Un-deletes" a row + * + * @param null $where + * + * @return bool + */ + public function restore($where = null) + { + $this->with_trashed(); + if (isset($where)) { + $this->where($where); + } + if ($affected_rows = $this->_database->update($this->table, array($this->_deleted_at_field => null))) { + $this->_prep_after_write(); + + return $affected_rows; + } + + return false; + } + + /** + * public function trashed($where = NULL) + * Verifies if a record (row) is soft_deleted or not + * + * @param null $where + * + * @return bool + */ + public function trashed($where = null) + { + $this->only_trashed(); + if (isset($where)) { + $this->where($where); + } + $this->limit(1); + $query = $this->_database->get($this->table); + if ($query->num_rows() == 1) { + return true; + } + + return false; + } + + public function _get_joined($requested) + { + $this->_database->join( + $this->_relationships[$requested['request']]['foreign_table'], + $this->table . '.' . $this->_relationships[$requested['request']]['local_key'] . ' = ' . $this->_relationships[$requested['request']]['foreign_table'] . '.' . $this->_relationships[$requested['request']]['foreign_key'] + ); + $the_select = ''; + if (!empty($requested['parameters'])) { + if (array_key_exists('fields', $requested['parameters'])) { + $fields = explode(',', $requested['parameters']['fields']); + $sub_select = array(); + foreach ($fields as $field) { + $sub_select[] = ((strpos( + $field, + '.' + ) === false) ? '`' . $this->_relationships[$requested['request']]['foreign_table'] . '`.`' . trim( + $field + ) . '`' : trim($field)) . ' AS ' . $requested['request'] . '_' . trim($field); + } + $the_select = implode(',', $sub_select); + } else { + $the_select = $this->_relationships[$requested['request']]['foreign_table'] . '.*'; + } + } + $this->_database->select($the_select); + unset($this->_requested[$requested['request']]); + } + + /** + * public function get() + * Retrieves one row from table. + * + * @param null $where + * + * @return mixed + */ + public function get($where = null) + { + $data = $this->_get_from_cache(); + if (isset($data) && $data !== false) { + $this->_database->reset_query(); + if (isset($this->_cache)) { + unset($this->_cache); + } + + return $data; + } else { + $this->trigger('before_get'); + if ($this->_select) { + $this->_database->select($this->_select); + } + if (!empty($this->_requested)) { + foreach ($this->_requested as $requested) { + if (isset($requested['parameters']['join'])) { + $this->_get_joined($requested); + } else { + $this->_database->select($this->_relationships[$requested['request']]['local_key']); + } + } + } + if (isset($where)) { + $this->where($where); + } elseif ($this->soft_deletes === true) { + $this->_where_trashed(); + } + $this->limit(1); + $query = $this->_database->get($this->table); + $this->_reset_trashed(); + if ($query->num_rows() == 1) { + $row = $query->row_array(); + $row = $this->trigger('after_get', $row); + $row = $this->_prep_after_read(array($row), false); + $row = $row[0]; + $this->_write_to_cache($row); + + return $row; + } else { + return false; + } + } + } + + /** + * public function get_all() + * Retrieves rows from table. + * + * @param null $where + * + * @return mixed + */ + public function get_all($where = null) + { + $data = $this->_get_from_cache(); + if (isset($data) && $data !== false) { + $this->_database->reset_query(); + if (isset($this->_cache)) { + unset($this->_cache); + } + + return $data; + } else { + $this->trigger('before_get'); + if (isset($where)) { + $this->where($where); + } elseif ($this->soft_deletes === true) { + $this->_where_trashed(); + } + if (isset($this->_select)) { + $this->_database->select($this->_select); + } + if (!empty($this->_requested)) { + foreach ($this->_requested as $requested) { + if (isset($requested['parameters']['join'])) { + $this->_get_joined($requested); + } else { + $this->_database->select($this->_relationships[$requested['request']]['local_key']); + } + } + } + $query = $this->_database->get($this->table); + $this->_reset_trashed(); + if ($query->num_rows() > 0) { + $data = $query->result_array(); + $data = $this->trigger('after_get', $data); + $data = $this->_prep_after_read($data, true); + $this->_write_to_cache($data); + + return $data; + } else { + return false; + } + } + } + + /** + * public function count_rows() + * Retrieves number of rows from table. + * + * @param null $where + * + * @return integer + */ + public function count_rows($where = null) + { + if (isset($where)) { + $this->where($where); + } elseif ($this->soft_deletes === true) { + $this->_where_trashed(); + } + $this->_database->from($this->table); + $number_rows = $this->_database->count_all_results(); + $this->_reset_trashed(); + + return $number_rows; + } + /** RELATIONSHIPS */ + /** + * public function with($requests) + * allows the user to retrieve records from other interconnected tables depending on the relations defined before + * the constructor + * + * @param string $request + * @param array $arguments + * + * @return $this + */ + public function with($request, $arguments = array()) + { + $this->_set_relationships(); + if (array_key_exists($request, $this->_relationships)) { + $this->_requested[$request] = array('request' => $request); + $parameters = array(); + if (isset($arguments)) { + foreach ($arguments as $argument) { + if (is_array($argument)) { + foreach ($argument as $k => $v) { + $parameters[$k] = $v; + } + } else { + $requested_operations = explode('|', $argument); + foreach ($requested_operations as $operation) { + $elements = explode(':', $operation, 2); + if (sizeof($elements) == 2) { + $parameters[$elements[0]] = $elements[1]; + } else { + show_error( + 'MY_Model: Parameters for with_*() method must be of the form: "...->with_*(\'where:...|fields:...\')"' + ); + } + } + } + } + } + $this->_requested[$request]['parameters'] = $parameters; + } + + /* + if($separate_subqueries === FALSE) + { + $this->separate_subqueries = FALSE; + foreach($this->_requested as $request) + { + if($this->_relationships[$request]['relation'] == 'has_one') $this->_has_one($request); + } + } + else + { + $this->after_get[] = 'join_temporary_results'; + } + */ + + return $this; + } + + /** + * protected function join_temporary_results($data) + * Joins the subquery results to the main $data + * + * @param $data + * + * @return mixed + */ + protected function join_temporary_results($data) + { + $order_by = array(); + $order_inside_array = array(); + //$order_inside = ''; + foreach ($this->_requested as $requested_key => $request) { + $pivot_table = null; + $relation = $this->_relationships[$request['request']]; + $this->load->model($relation['foreign_model'], $relation['foreign_model_name']); + $foreign_key = $relation['foreign_key']; + $local_key = $relation['local_key']; + $foreign_table = $relation['foreign_table']; + $type = $relation['relation']; + $relation_key = $relation['relation_key']; + if ($type == 'has_many_pivot') { + $pivot_table = $relation['pivot_table']; + $pivot_local_key = $relation['pivot_local_key']; + $pivot_foreign_key = $relation['pivot_foreign_key']; + $get_relate = $relation['get_relate']; + } + if (array_key_exists('order_inside', $request['parameters'])) { + //$order_inside = $request['parameters']['order_inside']; + $elements = explode(',', $request['parameters']['order_inside']); + foreach ($elements as $element) { + $order = explode(' ', $element); + if (sizeof($order) == 2) { + $order_inside_array[] = array( + trim($order[0]), + trim($order[1]) + ); + } else { + $order_inside_array[] = array( + trim($order[0]), + 'desc' + ); + } + } + } + $local_key_values = array(); + foreach ($data as $key => $element) { + if (isset($element[$local_key]) and !empty($element[$local_key])) { + $id = $element[$local_key]; + $local_key_values[$key] = $id; + } + } + if (!$local_key_values) { + $data[$key][$relation_key] = null; + continue; + } + if (!isset($pivot_table)) { + $sub_results = $this->{$relation['foreign_model_name']}; + $select = array(); + $select[] = '`' . $foreign_table . '`.`' . $foreign_key . '`'; + if (!empty($request['parameters'])) { + if (array_key_exists('fields', $request['parameters'])) { + if ($request['parameters']['fields'] == '*count*') { + $the_select = '*count*'; + $sub_results = (isset($the_select)) ? $sub_results->fields($the_select) : $sub_results; + $sub_results = $sub_results->fields($foreign_key); + } else { + $fields = explode(',', $request['parameters']['fields']); + foreach ($fields as $field) { + $select[] = (strpos($field, '.') === false) ? '`' . $foreign_table . '`.`' . trim( + $field + ) . '`' : trim($field); + } + $the_select = implode(',', $select); + $sub_results = (isset($the_select)) ? $sub_results->fields($the_select) : $sub_results; + } + } + if (array_key_exists( + 'fields', + $request['parameters'] + ) && ($request['parameters']['fields'] == '*count*')) { + $sub_results->group_by('`' . $foreign_table . '`.`' . $foreign_key . '`'); + } + if (array_key_exists('where', $request['parameters']) || array_key_exists( + 'non_exclusive_where', + $request['parameters'] + )) { + $the_where = array_key_exists( + 'where', + $request['parameters'] + ) ? 'where' : 'non_exclusive_where'; + } + $sub_results = isset($the_where) ? $sub_results->where( + $request['parameters'][$the_where], + null, + null, + false, + false, + true + ) : $sub_results; + if (isset($order_inside_array)) { + foreach ($order_inside_array as $order_by_inside) { + $sub_results = $sub_results->order_by($order_by_inside[0], $order_by_inside[1]); + } + } + //Add nested relation + if (array_key_exists('with', $request['parameters'])) { + // Do we have many nested relation + if (is_array( + $request['parameters']['with'] + ) && isset($request['parameters']['with'][0]) && is_array( + $request['parameters']['with'][0] + )) { + foreach ($request['parameters']['with'] as $with) { + $with_relation = array_shift($with); + $sub_results->with($with_relation, array($with)); + } + } else // single nested relation + { + $with_relation = array_shift($request['parameters']['with']); + $sub_results->with($with_relation, array($request['parameters']['with'])); + } + } + } + $sub_results = $sub_results->where($foreign_key, $local_key_values)->get_all(); + } else { + $this->_database->join( + $pivot_table, + $foreign_table . '.' . $foreign_key . ' = ' . $pivot_table . '.' . $pivot_foreign_key, + 'left' + ); + $this->_database->join( + $this->table, + $pivot_table . '.' . $pivot_local_key . ' = ' . $this->table . '.' . $local_key, + 'left' + ); + $this->_database->select($foreign_table . '.' . $foreign_key); + $this->_database->select($pivot_table . '.' . $pivot_local_key); + if (!empty($request['parameters'])) { + if (array_key_exists('fields', $request['parameters'])) { + if ($request['parameters']['fields'] == '*count*') { + $this->_database->select( + 'COUNT(`' . $foreign_table . '`.`' . $foreign_key . '`) as counted_rows, `' . $foreign_table . '`.`' . $foreign_key . '`', + false + ); + } else { + $fields = explode(',', $request['parameters']['fields']); + $select = array(); + foreach ($fields as $field) { + $select[] = (strpos($field, '.') === false) ? '`' . $foreign_table . '`.`' . trim( + $field + ) . '`' : trim($field); + } + $the_select = implode(',', $select); + $this->_database->select($the_select); + } + } + if (array_key_exists('where', $request['parameters']) || array_key_exists( + 'non_exclusive_where', + $request['parameters'] + )) { + $the_where = array_key_exists( + 'where', + $request['parameters'] + ) ? 'where' : 'non_exclusive_where'; + $this->_database->where($request['parameters'][$the_where], null, null, false, false, true); + } + } + $this->_database->where_in($pivot_table . '.' . $pivot_local_key, $local_key_values); + if (!empty($order_inside_array)) { + $order_inside_str = ''; + foreach ($order_inside_array as $order_by_inside) { + $order_inside_str .= (strpos( + $order_by_inside[0], + ',' + ) === false) ? '`' . $foreign_table . '`.`' . $order_by_inside[0] . ' ' . $order_by_inside[1] : $order_by_inside[0] . ' ' . $order_by_inside[1]; + $order_inside_str .= ','; + } + $order_inside_str = rtrim($order_inside_str, ","); + $this->_database->order_by(rtrim($order_inside_str, ",")); + } + $sub_results = $this->_database->get($foreign_table)->result_array(); + $this->_database->reset_query(); + } + if (isset($sub_results) && !empty($sub_results)) { + $subs = array(); + foreach ($sub_results as $result) { + $result_array = (array)$result; + $the_foreign_key = $result_array[$foreign_key]; + if (isset($pivot_table)) { + $the_local_key = $result_array[$pivot_local_key]; + if (isset($get_relate) and $get_relate === true) { + $subs[$the_local_key][$the_foreign_key] = $this->{$relation['foreign_model']}->where( + $foreign_key, + $result[$foreign_key] + )->get(); + } else { + $subs[$the_local_key][$the_foreign_key] = $result; + } + } else { + if ($type == 'has_one') { + $subs[$the_foreign_key] = $result; + } else { + $subs[$the_foreign_key][] = $result; + } + } + } + $sub_results = $subs; + foreach ($local_key_values as $key => $value) { + if (array_key_exists($value, $sub_results)) { + $data[$key][$relation_key] = $sub_results[$value]; + } else { + if (array_key_exists('where', $request['parameters'])) { + unset($data[$key]); + } + } + } + } else { + $data[$key][$relation_key] = null; + } + if (array_key_exists('order_by', $request['parameters'])) { + $elements = explode(',', $request['parameters']['order_by']); + if (sizeof($elements) == 2) { + $order_by[$relation_key] = array( + trim($elements[0]), + trim($elements[1]) + ); + } else { + $order_by[$relation_key] = array( + trim($elements[0]), + 'desc' + ); + } + } + unset($this->_requested[$requested_key]); + } + if (!empty($order_by)) { + foreach ($order_by as $field => $row) { + list($key, $value) = $row; + $data = $this->_build_sorter($data, $field, $key, $value); + } + } + + return $data; + } + + /** + * private function _has_one($request) + * + * returns a joining of two tables depending on the $request relationship established in the constructor + * + * @param $request + * + * @return $this + */ + private function _has_one($request) + { + $relation = $this->_relationships[$request]; + $this->_database->join( + $relation['foreign_table'], + $relation['foreign_table'] . '.' . $relation['foreign_key'] . ' = ' . $this->table . '.' . $relation['local_key'], + 'left' + ); + + return true; + } + + /** + * private function _set_relationships() + * + * Called by the public method with() it will set the relationships between the current model and other models + */ + private function _set_relationships() + { + if (empty($this->_relationships)) { + $options = array( + 'has_one', + 'has_many', + 'has_many_pivot' + ); + foreach ($options as $option) { + if (isset($this->{$option}) && !empty($this->{$option})) { + foreach ($this->{$option} as $key => $relation) { + $single_query = false; + if (!is_array($relation)) { + $foreign_model = $relation; + $model = $this->_parse_model_dir($foreign_model); + $foreign_model = $model['foreign_model']; + //$model_dir = $model['model_dir']; + $foreign_model_name = $model['foreign_model_name']; + $this->load->model($foreign_model, $foreign_model_name); + $foreign_table = $this->{$foreign_model_name}->table; + $foreign_key = $this->{$foreign_model_name}->primary_key; + $local_key = $this->primary_key; + $pivot_local_key = $this->table . '_' . $local_key; + $pivot_foreign_key = $foreign_table . '_' . $foreign_key; + $get_relate = false; + } else { + if ($this->is_assoc($relation)) { + $foreign_model = $relation['foreign_model']; + $model = $this->_parse_model_dir($foreign_model); + $foreign_model = $model['model_dir'] . $model['foreign_model']; + $foreign_model_name = $model['foreign_model_name']; + if (array_key_exists('foreign_table', $relation)) { + $foreign_table = $relation['foreign_table']; + } else { + $this->load->model($foreign_model, $foreign_model_name); + $foreign_table = $this->{$foreign_model_name}->table; + } + $foreign_key = $relation['foreign_key']; + $local_key = $relation['local_key']; + if ($option == 'has_many_pivot') { + $pivot_table = $relation['pivot_table']; + $pivot_local_key = (array_key_exists( + 'pivot_local_key', + $relation + )) ? $relation['pivot_local_key'] : $this->table . '_' . $this->primary_key; + $pivot_foreign_key = (array_key_exists( + 'pivot_foreign_key', + $relation + )) ? $relation['pivot_foreign_key'] : $foreign_table . '_' . $foreign_key; + $get_relate = (array_key_exists( + 'get_relate', + $relation + ) && ($relation['get_relate'] === true)) ? true : false; + } + if ($option == 'has_one' && isset($relation['join']) && $relation['join'] === true) { + $single_query = true; + } + } else { + $foreign_model = $relation[0]; + $model = $this->_parse_model_dir($foreign_model); + $foreign_model = $model['model_dir'] . $model['foreign_model']; + $foreign_model_name = $model['foreign_model_name']; + $this->load->model($foreign_model); + $foreign_table = $this->{$foreign_model}->table; + $foreign_key = $relation[1]; + $local_key = $relation[2]; + if ($option == 'has_many_pivot') { + $pivot_local_key = $this->table . '_' . $this->primary_key; + $pivot_foreign_key = $foreign_table . '_' . $foreign_key; + $get_relate = (isset($relation[3]) && ($relation[3] === TRUE())) ? true : false; + } + } + } + if ($option == 'has_many_pivot' && !isset($pivot_table)) { + $tables = array( + $this->table, + $foreign_table + ); + sort($tables); + $pivot_table = $tables[0] . '_' . $tables[1]; + } + $this->_relationships[$key] = array( + 'relation' => $option, + 'relation_key' => $key, + 'foreign_model' => bear_str_to_lower($foreign_model), + 'foreign_model_name' => bear_str_to_lower($foreign_model_name), + 'foreign_table' => $foreign_table, + 'foreign_key' => $foreign_key, + 'local_key' => $local_key + ); + if ($option == 'has_many_pivot') { + $this->_relationships[$key]['pivot_table'] = $pivot_table; + $this->_relationships[$key]['pivot_local_key'] = $pivot_local_key; + $this->_relationships[$key]['pivot_foreign_key'] = $pivot_foreign_key; + $this->_relationships[$key]['get_relate'] = $get_relate; + } + if ($single_query === true) { + $this->_relationships[$key]['joined'] = true; + } + } + } + } + } + } + /** END RELATIONSHIPS */ + /** + * public function on($connection_group = NULL) + * Sets a different connection to use for a query + * + * @param $connection_group = NULL - connection group in database setup + * + * @return obj + */ + public function on($connection_group = null) + { + if (isset($connection_group)) { + $this->_database->close(); + $this->load->database($connection_group); + $this->_database = $this->db; + } + + return $this; + } + + /** + * public function reset_connection($connection_group = NULL) + * Resets the connection to the default used for all the model + * + * @return obj + */ + public function reset_connection() + { + if (isset($connection_group)) { + $this->_database->close(); + $this->_set_connection(); + } + + return $this; + } + + /** + * Trigger an event and call its observers. Pass through the event name + * (which looks for an instance variable $this->event_name), an array of + * parameters to pass through and an optional 'last in interation' boolean + */ + public function trigger($event, $data = array(), $last = true) + { + if (isset($this->$event) && is_array($this->$event)) { + foreach ($this->$event as $method) { + if (strpos($method, '(')) { + preg_match('/([a-zA-Z0-9\_\-]+)(\(([a-zA-Z0-9\_\-\., ]+)\))?/', $method, $matches); + $method = $matches[1]; + $this->callback_parameters = explode(',', $matches[3]); + } + $data = call_user_func_array(array( + $this, + $method + ), array( + $data, + $last + )); + } + } + + return $data; + } + + /** + * private function _reset_trashed() + * Sets $_trashed to default 'without' + */ + private function _reset_trashed() + { + $this->_trashed = 'without'; + + return $this; + } + + /** + * public function with_trashed() + * Sets $_trashed to with + */ + public function with_trashed() + { + $this->_trashed = 'with'; + + return $this; + } + + /** + * public function without_trashed() + * Sets $_trashed to without + */ + public function without_trashed() + { + $this->_trashed = 'without'; + + return $this; + } + + /** + * public function with_trashed() + * Sets $_trashed to only + */ + public function only_trashed() + { + $this->_trashed = 'only'; + + return $this; + } + + private function _where_trashed() + { + switch ($this->_trashed) { + case 'only': + $this->_database->where( + $this->table . '.' . $this->_deleted_at_field . ' IS NOT NULL', + null, + false + ); + break; + case 'without': + $this->_database->where($this->table . '.' . $this->_deleted_at_field . ' IS NULL', null, false); + break; + case 'with': + break; + } + + //$this->_trashed = ''; issue #208... + return $this; + } + + /** + * public function fields($fields) + * does a select() of the $fields + * + * @param $fields the fields needed + * + * @return $this + */ + public function fields($fields = null) + { + if (isset($fields)) { + if ($fields == '*count*') { + $this->_select = ''; + $this->_database->select('COUNT(*) AS counted_rows', false); + } else { + $this->_select = array(); + $fields = (!is_array($fields)) ? explode(',', $fields) : $fields; + if (!empty($fields)) { + foreach ($fields as &$field) { + $exploded = explode('.', $field); + if (sizeof($exploded) < 2) { + $field = $this->table . '.' . $field; + } + } + } + $this->_select = $fields; + } + } else { + $this->_select = null; + } + + return $this; + } + + /** + * public function order_by($criteria, $order = 'ASC' + * A wrapper to $this->_database->order_by() + * + * @param $criteria + * @param string $order + * + * @return $this + */ + public function order_by($criteria, $order = 'ASC') + { + if (is_array($criteria)) { + foreach ($criteria as $key => $value) { + $this->_database->order_by($key, $value); + } + } else { + $this->_database->order_by($criteria, $order); + } + + return $this; + } + + /** + * Return the next call as an array rather than an object + */ + public function as_array() + { + $this->return_as = 'array'; + + return $this; + } + + /** + * Return the next call as an object rather than an array + */ + public function as_object() + { + $this->return_as = 'object'; + + return $this; + } + + public function as_dropdown($field = null) + { + if (!isset($field)) { + show_error( + 'MY_Model: You must set a field to be set as value for the key: ...->as_dropdown(\'field\')->...' + ); + exit; + } + $this->return_as_dropdown = 'dropdown'; + $this->_dropdown_field = $field; + $this->_select = array( + $this->primary_key, + $field + ); + + return $this; + } + + protected function _get_from_cache($cache_name = null) + { + if (isset($cache_name) || (isset($this->_cache) && !empty($this->_cache))) { + $this->load->driver('cache'); + $cache_name = isset($cache_name) ? $cache_name : $this->_cache['cache_name']; + $data = $this->cache->{$this->cache_driver}->get($cache_name); + + return $data; + } + } + + protected function _write_to_cache($data, $cache_name = null) + { + if (isset($cache_name) || (isset($this->_cache) && !empty($this->_cache))) { + $this->load->driver('cache'); + $cache_name = isset($cache_name) ? $cache_name : $this->_cache['cache_name']; + $seconds = $this->_cache['seconds']; + if (isset($cache_name) && isset($seconds)) { + $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); + $this->_reset_cache($cache_name); + + return true; + } + + return false; + } + } + + public function set_cache($string, $seconds = 86400) + { + $prefix = (bear_str_length($this->cache_prefix) > 0) ? $this->cache_prefix . '_' : ''; + $prefix .= $this->table . '_'; + $this->_cache = array( + 'cache_name' => $prefix . $string, + 'seconds' => $seconds + ); + + return $this; + } + + private function _reset_cache($string) + { + if (isset($string)) { + $this->_cache = array(); + } + + return $this; + } + + public function delete_cache($string = null) + { + $this->load->driver('cache'); + $prefix = (bear_str_length($this->cache_prefix) > 0) ? $this->cache_prefix . '_' : ''; + $prefix .= $this->table . '_'; + if (isset($string) && (strpos($string, '*') === false)) { + $this->cache->{$this->cache_driver}->delete($prefix . $string); + } else { + $cached = $this->cache->file->cache_info(); + foreach ($cached as $file) { + if (array_key_exists('relative_path', $file)) { + $path = $file['relative_path']; + break; + } + } + $mask = (isset($string)) ? $path . $prefix . $string : $path . $this->cache_prefix . '_*'; + array_map('unlink', glob($mask)); + } + + return $this; + } + + /** + * private function _set_timestamps() + * + * Sets the fields for the created_at, updated_at and deleted_at timestamps + * + * @return bool + */ + private function _set_timestamps() + { + if ($this->timestamps !== false) { + $this->_created_at_field = (is_array( + $this->timestamps + ) && isset($this->timestamps[0])) ? $this->timestamps[0] : 'created_at'; + $this->_updated_at_field = (is_array( + $this->timestamps + ) && isset($this->timestamps[1])) ? $this->timestamps[1] : 'updated_at'; + $this->_deleted_at_field = (is_array( + $this->timestamps + ) && isset($this->timestamps[2])) ? $this->timestamps[2] : 'deleted_at'; + } + + return true; + } + + /** + * private function _the_timestamp() + * + * returns a value representing the date/time depending on the timestamp format choosed + * + * @return string + */ + private function _the_timestamp() + { + if ($this->timestamps_format == 'timestamp') { + return time(); + } else { + return date($this->timestamps_format); + } + } + + /** + * private function _set_connection() + * + * Sets the connection to database + */ + private function _set_connection() + { + if (isset($this->_database_connection)) { + $this->_database = $this->load->database($this->_database_connection, true); + } else { + $this->load->database(); + $this->_database = $this->db; + } + + // This may not be required + return $this; + } + + /* + * HELPER FUNCTIONS + */ + public function paginate($rows_per_page, $total_rows = null, $page_number = 1) + { + $this->load->helper('url'); + $segments = $this->uri->total_segments(); + $uri_array = $this->uri->segment_array(); + $page = $this->uri->segment($segments); + if (is_numeric($page)) { + $page_number = $page; + } else { + $page_number = $page_number; + $uri_array[] = $page_number; + ++$segments; + } + $next_page = $page_number + 1; + $previous_page = $page_number - 1; + if ($page_number == 1) { + $this->previous_page = $this->pagination_delimiters[0] . $this->pagination_arrows[0] . $this->pagination_delimiters[1]; + } else { + $uri_array[$segments] = $previous_page; + $uri_string = implode('/', $uri_array); + $this->previous_page = $this->pagination_delimiters[0] . anchor( + $uri_string, + $this->pagination_arrows[0] + ) . $this->pagination_delimiters[1]; + } + $uri_array[$segments] = $next_page; + $uri_string = implode('/', $uri_array); + if (isset($total_rows) && (ceil($total_rows / $rows_per_page) == $page_number)) { + $this->next_page = $this->pagination_delimiters[0] . $this->pagination_arrows[1] . $this->pagination_delimiters[1]; + } else { + $this->next_page = $this->pagination_delimiters[0] . anchor( + $uri_string, + $this->pagination_arrows[1] + ) . $this->pagination_delimiters[1]; + } + $rows_per_page = (is_numeric($rows_per_page)) ? $rows_per_page : 10; + if (isset($total_rows)) { + if ($total_rows != 0) { + $number_of_pages = ceil($total_rows / $rows_per_page); + $links = $this->previous_page; + for ($i = 1; $i <= $number_of_pages; $i++) { + unset($uri_array[$segments]); + $uri_string = implode('/', $uri_array); + $links .= $this->pagination_delimiters[0]; + $links .= (($page_number == $i) ? anchor($uri_string, $i) : anchor($uri_string . '/' . $i, $i)); + $links .= $this->pagination_delimiters[1]; + } + $links .= $this->next_page; + $this->all_pages = $links; + } else { + $this->all_pages = $this->pagination_delimiters[0] . $this->pagination_delimiters[1]; + } + } + if (isset($this->_cache) && !empty($this->_cache)) { + $this->load->driver('cache'); + $cache_name = $this->_cache['cache_name'] . '_' . $page_number; + $seconds = $this->_cache['seconds']; + $data = $this->cache->{$this->cache_driver}->get($cache_name); + } + if (isset($data) && $data !== false) { + return $data; + } else { + $this->trigger('before_get'); + $this->where(); + $this->limit($rows_per_page, (($page_number - 1) * $rows_per_page)); + $data = $this->get_all(); + if ($data) { + if (isset($cache_name) && isset($seconds)) { + $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); + $this->_reset_cache($cache_name); + } + + return $data; + } else { + return false; + } + } + } + + public function set_pagination_delimiters($delimiters) + { + if (is_array($delimiters) && sizeof($delimiters) == 2) { + $this->pagination_delimiters = $delimiters; + } + + return $this; + } + + public function set_pagination_arrows($arrows) + { + if (is_array($arrows) && sizeof($arrows) == 2) { + $this->pagination_arrows = $arrows; + } + + return $this; + } + + /** + * private function _fetch_table() + * + * Sets the table name when called by the constructor + * + */ + private function _fetch_table() + { + if (!isset($this->table)) { + $this->table = $this->_get_table_name(get_class($this)); + } + + return true; + } + + private function _get_table_name($model_name) + { + $model_name = bear_str_to_lower($model_name); + $table_name = plural(preg_replace('/(_m|_model|_mdl)?$/', '', $model_name)); + + return $table_name; + } + + public function __call($method, $arguments) + { + if (substr($method, 0, 6) == 'where_') { + $column = substr($method, 6); + $this->where($column, $arguments); + + return $this; + } + if (($method != 'with_trashed') && (substr($method, 0, 5) == 'with_')) { + $relation = substr($method, 5); + $this->with($relation, $arguments); + + return $this; + } + if (method_exists($this->_database, $method)) { + call_user_func_array(array( + $this->_database, + $method + ), $arguments); + + return $this; + } + $parent_class = get_parent_class($this); + if ($parent_class !== false && !method_exists($parent_class, $method) && !method_exists($this, $method)) { + $msg = 'The method "' . $method . '" does not exist in ' . get_class( + $this + ) . ' or MY_Model or CI_Model.'; + show_error($msg, EXIT_UNKNOWN_METHOD, 'Method Not Found'); + } + } + + private function _build_sorter($data, $field, $order_by, $sort_by = 'DESC') + { + usort($data, function ($a, $b) use ($field, $order_by, $sort_by) { + $array_a = isset($a[$field]) ? $this->object_to_array($a[$field]) : null; + $array_b = isset($b[$field]) ? $this->object_to_array($b[$field]) : null; + + return strtoupper( + $sort_by + ) == "DESC" ? ((isset($array_a[$order_by]) && isset($array_b[$order_by])) ? ($array_a[$order_by] < $array_b[$order_by]) : (!isset($array_a) ? 1 : -1)) : ((isset($array_a[$order_by]) && isset($array_b[$order_by])) ? ($array_a[$order_by] > $array_b[$order_by]) : (!isset($array_b) ? 1 : -1)); + }); + + return $data; + } + + public function object_to_array($object) + { + if (!is_object($object) && !is_array($object)) { + return $object; + } + if (is_object($object)) { + $object = get_object_vars($object); + } + + return array_map(array( + $this, + 'object_to_array' + ), $object); + } + + /** + * Verifies if an array is associative or not + * + * @param array $array + * + * @return bool + */ + protected function is_assoc(array $array) + { + return (bool)count(array_filter(array_keys($array), 'is_string')); + } + + /** + * private function _parse_model_dir($foreign_model) + * + * Parse model and model folder + * + * @param $foreign_model + * + * @return $data + */ + private function _parse_model_dir($foreign_model) + { + $data['foreign_model'] = $foreign_model; + $data['model_dir'] = ''; + $full_model = explode('/', $data['foreign_model']); + if ($full_model) { + $data['foreign_model'] = end($full_model); + $data['model_dir'] = str_replace($data['foreign_model'], null, implode('/', $full_model)); + } + $foreign_model_name = str_replace('/', '_', $data['model_dir'] . $data['foreign_model']); + $foreign_model_name = bear_str_to_lower($foreign_model_name); + + $data['foreign_model_name'] = $foreign_model_name; + + return $data; + } + /* + public function add_creator($data) + { + $data['created_by'] = $_SESSION['user_id']; + return $data; + } + */ + /* + public function add_updater($data) + { + $data['updated_by'] = $_SESSION['user_id']; + return $data; + } + */ + } } diff --git a/hungng/HungNG_ORM_Model.php b/hungng/HungNG_ORM_Model.php index 98ba3d7..21ac4e8 100644 --- a/hungng/HungNG_ORM_Model.php +++ b/hungng/HungNG_ORM_Model.php @@ -2,1800 +2,1811 @@ defined('BASEPATH') or exit('No direct script access allowed'); if (!class_exists('HungNG_ORM_Model')) { - /** - * Project codeigniter-framework - * Created by PhpStorm - * User: 713uk13m - * Copyright: 713uk13m - * Date: 23/06/2022 - * Time: 01:46 - */ - class HungNG_ORM_Model extends CI_Model implements ArrayAccess - { - /** - * Database Configuration for read-write master - * - * @var object|string|array CI DB ($this->db as default), CI specific group name or CI database config array - */ - protected $database = ""; - - /** - * Database Configuration for read-only slave - * - * @var object|string|array CI DB ($this->db as default), CI specific group name or CI database config array - */ - protected $databaseRead = ""; - - /** - * Table name - * - * @var string - */ - protected $table = ""; - - /** - * Table alias name - * - * @var string - */ - protected $alias = null; - - /** - * Primary key of table - * - * @var string Field name of single column primary key - */ - protected $primaryKey = 'id'; - - /** - * Fillable columns of table - * - * @var array Field names of columns - */ - protected $fillable = array(); - - /** - * Indicates if the model should be timestamped. - * - * @var bool - */ - protected $timestamps = true; - - /** - * Date format for timestamps. - * - * @var string unixtime|datetime - */ - protected $dateFormat = 'datetime'; - - /** - * @string Feild name for created_at, empty is disabled. - */ - const CREATED_AT = 'created_at'; - - /** - * @string Feild name for updated_at, empty is disabled. - */ - const UPDATED_AT = 'updated_at'; - - /** - * CREATED_AT triggers UPDATED_AT. - * - * @var bool - */ - protected $createdWithUpdated = true; - - /** - * @var string Feild name for SOFT_DELETED, empty is disabled. - */ - const SOFT_DELETED = ''; - - /** - * The active value for SOFT_DELETED - * - * @var mixed - */ - protected $softDeletedFalseValue = '0'; - - /** - * The deleted value for SOFT_DELETED - * - * @var mixed - */ - protected $softDeletedTrueValue = '1'; - - /** - * This feature is actvied while having SOFT_DELETED - * - * @var string Feild name for deleted_at, empty is disabled. - */ - const DELETED_AT = ''; - - /** - * Check property schema for write - * - * @var boolean - */ - protected $propertyCheck = false; - - /** - * @var array Validation errors (depends on validator driver) - */ - protected $_errors; - - /** - * @var object database connection for write - */ - protected $_db; - - /** - * @var object database connection for read (Salve) - */ - protected $_dbr; - - /** - * @var object database caches by database key for write - */ - protected static $_dbCaches = array(); - - /** - * @var object database caches by database key for read (Salve) - */ - protected static $_dbrCaches = array(); - - /** - * @var object ORM schema caches by model class namespace - */ - private static $_ormCaches = array(); - - /** - * @var bool SOFT_DELETED one time switch - */ - private $_withoutSoftDeletedScope = false; - - /** - * @var bool Global Scope one time switch - */ - private $_withoutGlobalScope = false; - - /** - * ORM read properties - * - * @var array - */ - private $_readProperties = array(); - - /** - * ORM write properties - * - * @var array - */ - private $_writeProperties = array(); - - /** - * ORM self query - * - * @var string - */ - private $_selfCondition = null; - - /** - * Clean next find one time setting - * - * @var boolean - */ - private $_cleanNextFind = false; - - /** - * Relationship property caches by method name - * - * @var array - */ - private $_relationshipCaches = array(); - - /** - * Constructor - */ - public function __construct() - { - parent::__construct(); - - log_message('info', 'HungNG_ORM_Model Class Initialized'); - - /* Database Connection Setting */ - // Master - if ($this->database) { - if (is_object($this->database)) { - // CI DB Connection - $this->_db = $this->database; - } elseif (is_string($this->database)) { - // Cache Mechanism - if (isset(self::$_dbCaches[$this->database])) { - $this->_db = self::$_dbCaches[$this->database]; - } else { - // CI Database Configuration - $this->_db = get_instance()->load->database($this->database, true); - self::$_dbCaches[$this->database] = $this->_db; - } - } else { - // Config array for each Model - $this->_db = get_instance()->load->database($this->database, true); - } - } else { - // CI Default DB Connection - $this->_db = $this->_getDefaultDB(); - } - // Slave - if ($this->databaseRead) { - if (is_object($this->databaseRead)) { - // CI DB Connection - $this->_dbr = $this->databaseRead; - } elseif (is_string($this->databaseRead)) { - // Cache Mechanism - if (isset(self::$_dbrCaches[$this->databaseRead])) { - $this->_dbr = self::$_dbrCaches[$this->databaseRead]; - } else { - // CI Database Configuration - $this->_dbr = get_instance()->load->database($this->databaseRead, true); - self::$_dbrCaches[$this->databaseRead] = $this->_dbr; - } - } else { - // Config array for each Model - $this->_dbr = get_instance()->load->database($this->databaseRead, true); - } - } else { - // CI Default DB Connection - $this->_dbr = $this->_getDefaultDB(); - } - - /* Table Name Guessing */ - if (!$this->table) { - $this->table = str_replace('_model', '', bear_str_to_lower(get_called_class())); - } - } - - /** - * Get Master Database Connection - * - * @return object CI &DB - */ - public function getDatabase() - { - return $this->_db; - } - - /** - * Get Slave Database Connection - * - * @return object CI &DB - */ - public function getDatabaseRead() - { - return $this->_dbr; - } - - /** - * Alias of getDatabase() - */ - public function getDB() - { - return $this->getDatabase(); - } - - /** - * Alias of getDatabaseRead() - */ - public function getDBR() - { - return $this->getDatabaseRead(); - } - - /** - * Alias of getDatabaseRead() - */ - public function getBuilder() - { - return $this->getDatabaseRead(); - } - - /** - * Get table name - * - * @return string Table name - */ - public function getTable() - { - return $this->table; - } - - /** - * Alias of getTable() - */ - public function tableName() - { - return $this->getTable(); - } - - /** - * Returns the filter rules for validation. - * - * @return array Filter rules. [[['attr1','attr2'], 'callable'],] - */ - public function filters() - { - return array(); - } - - /** - * Returns the validation rules for attributes. - * - * @see https://www.codeigniter.com/userguide3/libraries/form_validation.html#rule-reference - * @return array validation rules. (CodeIgniter Rule Reference) - */ - public function rules() - { - return array(); - } - - /** - * Performs the data validation with filters - * - * ORM only performs validation for assigned properties. - * - * @param array Data of attributes - * @param boolean Return filtered data - * - * @return boolean Result - * @return mixed Data after filter ($returnData is true) - */ - public function validate($attributes = array(), $returnData = false) - { - // Data fetched by ORM or input - $data = ($attributes) ? $attributes : $this->_writeProperties; - // Filter first - $data = $this->filter($data); - // ORM re-assign properties - $this->_writeProperties = (!$attributes) ? $data : $this->_writeProperties; - // Get validation rules from function setting - $rules = $this->rules(); - - // The ORM update will only collect rules with corresponding modified attributes. - if ($this->_selfCondition) { - $newRules = array(); - foreach ((array)$rules as $key => $rule) { - if (isset($this->_writeProperties[$rule['field']])) { - // Add into new rules for updating - $newRules[] = $rule; - } - } - // Replace with mapping rules - $rules = $newRules; - } - - // Check if has rules - if (empty($rules)) { - return ($returnData) ? $data : true; - } - - // CodeIgniter form_validation doesn't work with empty array data - if (empty($data)) { - return false; - } - - // Load CodeIgniter form_validation library for yidas/model namespace, which has no effect on common one - get_instance()->load->library('form_validation', null, 'yidas_model_form_validation'); - // Get CodeIgniter validator - $validator = get_instance()->yidas_model_form_validation; - $validator->reset_validation(); - $validator->set_data($data); - $validator->set_rules($rules); - // Run Validate - $result = $validator->run(); - - // Result handle - if ($result === false) { - $this->_errors = $validator->error_array(); - - return false; - } else { - return ($returnData) ? $data : true; - } - } - - /** - * Validation - Get error data referenced by last failed Validation - * - * @return array - */ - public function getErrors() - { - return $this->_errors; - } - - /** - * Validation - Reset errors - * - * @return boolean - */ - public function resetErrors() - { - $this->_errors = null; - - return true; - } - - /** - * Filter process - * - * @param array $data Attributes - * - * @return array Filtered data - */ - public function filter($data) - { - // Get filter rules - $filters = $this->filters(); - - // Filter process with setting check - if (!empty($filters) && is_array($filters)) { - foreach ($filters as $key => $filter) { - if (!isset($filter[0])) { - throw new Exception( - "No attributes defined in \$filters from " . get_called_class() . " (" . __CLASS__ . ")", - 500 - ); - } - - if (!isset($filter[1])) { - throw new Exception( - "No function defined in \$filters from " . get_called_class() . " (" . __CLASS__ . ")", 500 - ); - } - - list($attributes, $function) = $filter; - - $attributes = (is_array($attributes)) ? $attributes : [$attributes]; - - // Filter each attribute - foreach ($attributes as $key2 => $attribute) { - if (!isset($data[$attribute])) { - continue; - } - - $data[$attribute] = call_user_func($function, $data[$attribute]); - } - } - } - - return $data; - } - - /** - * Set table alias for next find() - * - * @param string Table alias name - * - * @return $this - */ - public function setAlias($alias) - { - $this->alias = $alias; - - // Turn off cleaner to prevent continuous setting - $this->_cleanNextFind = false; - - return $this; - } - - /** - * Create an existent CI Query Builder instance with Model features for query purpose. - * - * @param boolean $withAll withAll() switch helper - * - * @return \CI_DB_query_builder CI_DB_query_builder - * @example - * $posts = $this->PostModel->find() - * ->where('is_public', '1') - * ->limit(0,25) - * ->order_by('id') - * ->get() - * ->result_array(); - * @example - * // Without all featured conditions for next find() - * $posts = $this->PostModel->find(true) - * ->where('is_deleted', '1') - * ->get() - * ->result_array(); - * // This is equal to withAll() method - * $this->PostModel->withAll()->find(); - * - */ - public function find($withAll = false) - { - $instance = (isset($this)) ? $this : new static; - - // One time setting reset mechanism - if ($instance->_cleanNextFind === true) { - // Reset alias - $instance->setAlias(null); - } else { - // Turn on clean for next find - $instance->_cleanNextFind = true; - } - - // Alias option for FROM - $sqlFrom = ($instance->alias) ? "{$instance->table} AS {$instance->alias}" : $instance->table; - - $instance->_dbr->from($sqlFrom); - - // WithAll helper - if ($withAll === true) { - $instance->withAll(); - } - - // Scope condition - $instance->_addGlobalScopeCondition(); - - // Soft Deleted condition - $instance->_addSoftDeletedCondition(); - - return $instance->_dbr; - } - - /** - * Create an CI Query Builder instance without Model Filters for query purpose. - * - * @return \CI_DB_query_builder CI_DB_query_builder - */ - public function forceFind() - { - return $this->withAll()->find(); - } - - /** - * Return a single active record model instance by a primary key or an array of column values. - * - * @param mixed $condition Refer to _findByCondition() for the explanation of this parameter - * - * @return object ActiveRecord(Model) - * @throws \Exception - * @example - * // Query builder ORM usage - * $this->Model->find()->where('id', 123); - * $this->Model->findOne(); - * @example - * $post = $this->Model->findOne(123); - */ - public static function findOne($condition) - { - if ((isset($this))) { - $instance = $this; - } else { - $instance = new static; - } - - $record = $instance->_findByCondition($condition) - ->limit(1) - ->get()->row_array(); - - // Record check - if (!$record) { - return $record; - } - - return $instance->createActiveRecord($record, $record[$instance->primaryKey]); - } - - /** - * Returns a list of active record models that match the specified primary key value(s) or a set of column values. - * - * @param mixed $condition Refer to _findByCondition() for the explanation - * @param integer|array $limit Limit or [offset, limit] - * - * @return array Set of ActiveRecord(Model)s - * @throws \Exception - * @example - * // Query builder ORM usage - * $this->Model->find()->where_in('id', [3,21,135]); - * $this->Model->findAll(); - * @example - * $post = $this->PostModel->findAll([3,21,135]); - */ - public static function findAll($condition = array(), $limit = null) - { - $instance = (isset($this)) ? $this : new static; - - $query = $instance->_findByCondition($condition); - - // Limit / offset - if ($limit) { - $offset = null; - - if (is_array($limit) && isset($limit[1])) { - // Prevent list() variable effect - $set = $limit; - list($offset, $limit) = $set; - } - - $query = ($limit) ? $query->limit($limit) : $query; - $query = ($offset) ? $query->offset($offset) : $query; - } - - $records = $query->get()->result_array(); - - // Record check - if (!$records) { - return $records; - } - - $set = array(); - // Each ActiveRecord - foreach ((array)$records as $key => $record) { - // Check primary key setting - if (!isset($record[$instance->primaryKey])) { - throw new Exception("Model's primary key not set", 500); - } - // Create an ActiveRecord into collect - $set[] = $instance->createActiveRecord($record, $record[$instance->primaryKey]); - } - - return $set; - } - - /** - * reset an CI Query Builder instance with Model. - * - * @return $this - * @example - * $this->Model->reset()->find(); - */ - public function reset() - { - // Reset query - $this->_db->reset_query(); - $this->_dbr->reset_query(); - - return $this; - } - - /** - * Insert a row with Timestamps feature into the associated database table using the attribute values of this record. - * - * @param array $attributes - * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. - * - * @return boolean Result - * @example - * $result = $this->Model->insert([ - * 'name' => 'Nick Tsai', - * 'email' => 'myintaer@gmail.com', - * ]); - */ - public function insert($attributes, $runValidation = true) - { - // Validation - if ($runValidation && false === $attributes = $this->validate($attributes, true)) { - return false; - } - - $this->_attrEventBeforeInsert($attributes); - - if ($this->fillable) { - $attributes = array_intersect_key($attributes, array_flip($this->fillable)); - } - - return $this->_db->insert($this->table, $attributes); - } - - /** - * Insert a batch of rows with Timestamps feature into the associated database table using the attribute values of this record. - * - * @param array $data The rows to be batch inserted - * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. - * - * @return int Number of rows inserted or FALSE on failure - * @example - * $result = $this->Model->batchInsert([ - * ['name' => 'Nick Tsai', 'email' => 'myintaer@gmail.com'], - * ['name' => 'Yidas', 'email' => 'service@yidas.com'] - * ]); - */ - public function batchInsert($data, $runValidation = true) - { - foreach ($data as $key => &$attributes) { - // Validation - if ($runValidation && false === $attributes = $this->validate($attributes, true)) { - return false; - } - - $this->_attrEventBeforeInsert($attributes); - } - - return $this->_db->insert_batch($this->table, $data); - } - - /** - * Get the insert ID number when performing database inserts. - * - * @param string $name Name of the sequence object from which the ID should be returned. - * - * @return integer Last insert ID - */ - public function getLastInsertID($name = null) - { - return $this->getDB()->insert_id($name); - } - - /** - * Replace a row with Timestamps feature into the associated database table using the attribute values of this record. - * - * @param array $attributes - * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. - * - * @return bool Result - * @example - * $result = $this->Model->replace([ - * 'id' => 1, - * 'name' => 'Nick Tsai', - * 'email' => 'myintaer@gmail.com', - * ]); - */ - public function replace($attributes, $runValidation = true) - { - // Validation - if ($runValidation && false === $attributes = $this->validate($attributes, true)) { - return false; - } - - $this->_attrEventBeforeInsert($attributes); - - return $this->_db->replace($this->table, $attributes); - } - - /** - * Save the changes with Timestamps feature to the selected record(s) into the associated database table. - * - * @param array $attributes - * @param mixed $condition Refer to _findByCondition() for the explanation - * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. - * - * @return bool Result - * - * @example - * $this->Model->update(['status'=>'off'], 123) - * @example - * // Query builder ORM usage - * $this->Model->find()->where('id', 123); - * $this->Model->update(['status'=>'off']); - */ - public function update($attributes, $condition = null, $runValidation = true) - { - // Validation - if ($runValidation && false === $attributes = $this->validate($attributes, true)) { - return false; - } - - // Model Condition - $query = $this->_findByCondition($condition); - - $attributes = $this->_attrEventBeforeUpdate($attributes); - - if ($this->fillable) { - $attributes = array_intersect_key($attributes, array_flip($this->fillable)); - } - - // Pack query then move it to write DB from read DB - $sql = $this->_dbr->set($attributes)->get_compiled_update(); - $this->_dbr->reset_query(); - - return $this->_db->query($sql); - } - - /** - * Update a batch of update queries into combined query strings. - * - * @param array $dataSet [[[Attributes], [Condition]], ] - * @param boolean $withAll withAll() switch helper - * @param integer $maxLenth MySQL max_allowed_packet - * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. - * - * @return integer Count of successful query pack(s) - * @example - * $result = $this->Model->batchUpdate([ - * [['title'=>'A1', 'modified'=>'1'], ['id'=>1]], - * [['title'=>'A2', 'modified'=>'1'], ['id'=>2]], - * ];); - */ - public function batchUpdate(array $dataSet, $withAll = false, $maxLength = null, $runValidation = true) - { - $maxLength = $maxLength ?: 4 * 1024 * 1024; - - $count = 0; - $sqlBatch = ''; - - foreach ($dataSet as $key => &$each) { - // Data format - list($attributes, $condition) = $each; - - // Check attributes - if (!is_array($attributes) || !$attributes) { - continue; - } - - // Validation - if ($runValidation && false === $attributes = $this->validate($attributes, true)) { - continue; - } - - // WithAll helper - if ($withAll === true) { - $this->withAll(); - } - - // Model Condition - $query = $this->_findByCondition($condition); - - $attributes = $this->_attrEventBeforeUpdate($attributes); - - // Pack query then move it to write DB from read DB - $sql = $this->_dbr->set($attributes)->get_compiled_update(); - $this->_dbr->reset_query(); - - // Last batch check: First single query & Max length - // The first single query needs to be sent ahead to prevent the limitation that PDO transaction could not - // use multiple SQL line in one query, but allows if the multi-line query is behind a single query. - if (($count == 0 && $sqlBatch) || bear_str_length($sqlBatch) >= $maxLength) { - // Each batch of query - $result = $this->_db->query($sqlBatch); - $sqlBatch = ""; - $count = ($result) ? $count + 1 : $count; - } - - // Keep Combining query - $sqlBatch .= "{$sql};\n"; - } - - // Last batch of query - $result = $this->_db->query($sqlBatch); - - return ($result) ? $count + 1 : $count; - } - - /** - * Delete the selected record(s) with Timestamps feature into the associated database table. - * - * @param mixed $condition Refer to _findByCondition() for the explanation - * @param boolean $forceDelete Force to hard delete - * @param array $attributes Extended attributes for Soft Delete Mode - * - * @return bool Result - * - * @example - * $this->Model->delete(123); - * @example - * // Query builder ORM usage - * $this->Model->find()->where('id', 123); - * $this->Model->delete(); - * @example - * // Force delete for SOFT_DELETED mode - * $this->Model->delete(123, true); - */ - public function delete($condition = null, $forceDelete = false, $attributes = array()) - { - // Check is Active Record - if ($this->_readProperties) { - // Reset condition and find single by self condition - $this->reset(); - $condition = $this->_selfCondition; - } - - // Model Condition by $forceDelete switch - $query = ($forceDelete) - ? $this->withTrashed()->_findByCondition($condition) - : $this->_findByCondition($condition); - - /* Soft Delete Mode */ - if (static::SOFT_DELETED - && isset($this->softDeletedTrueValue) - && !$forceDelete) { - // Mark the records as deleted - $attributes[static::SOFT_DELETED] = $this->softDeletedTrueValue; - - $attributes = $this->_attrEventBeforeDelete($attributes); - - // Pack query then move it to write DB from read DB - $sql = $this->_dbr->set($attributes)->get_compiled_update(); - $this->_dbr->reset_query(); - } else { - /* Hard Delete */ - // Pack query then move it to write DB from read DB - $sql = $this->_dbr->get_compiled_delete(); - $this->_dbr->reset_query(); - } - - return $this->_db->query($sql); - } - - /** - * Force Delete the selected record(s) with Timestamps feature into the associated database table. - * - * @param mixed $condition Refer to _findByCondition() for the explanation - * - * @return mixed CI delete result of DB Query Builder - * - * @example - * $this->Model->forceDelete(123) - * @example - * // Query builder ORM usage - * $this->Model->find()->where('id', 123); - * $this->Model->forceDelete(); - */ - public function forceDelete($condition = null) - { - return $this->delete($condition, true); - } - - /** - * Get the number of affected rows when doing “write” type queries (insert, update, etc.). - * - * @return integer Last insert ID - */ - public function getAffectedRows() - { - return $this->getDB()->affected_rows(); - } - - /** - * Restore SOFT_DELETED field value to the selected record(s) into the associated database table. - * - * @param mixed $condition Refer to _findByCondition() for the explanation - * - * @return bool Result - * - * @example - * $this->Model->restore(123) - * @example - * // Query builder ORM usage - * $this->Model->withTrashed()->find()->where('id', 123); - * $this->Model->restore(); - */ - public function restore($condition = null) - { - // Model Condition with Trashed - $query = $this->withTrashed()->_findByCondition($condition); - - /* Soft Delete Mode */ - if (static::SOFT_DELETED - && isset($this->softDeletedFalseValue)) { - // Mark the records as deleted - $attributes[static::SOFT_DELETED] = $this->softDeletedFalseValue; - - return $query->update($this->table, $attributes); - } else { - return false; - } - } - - /** - * Get count from query - * - * @param boolean Reset query conditions - * - * @return integer - */ - public function count($resetQuery = true) - { - return $this->getDBR()->count_all_results('', $resetQuery); - } - - /** - * Lock the selected rows in the table for updating. - * - * sharedLock locks only for write, lockForUpdate also prevents them from being selected - * - * @return object CI_DB_result - * @example - * // This transaction block will lock selected rows for next same selected - * // rows with `FOR UPDATE` lock: - * $this->Model->getDB()->trans_start(); - * $this->Model->find()->where('id', 123) - * $result = $this->Model->lockForUpdate()->row_array(); - * $this->Model->getDB()->trans_complete(); - * - * @example - * $this->Model->find()->where('id', 123) - * $result = $this->Model->lockForUpdate()->row_array(); - */ - public function lockForUpdate() - { - // Pack query then move it to write DB from read DB for transaction - $sql = $this->_dbr->get_compiled_select(); - $this->_dbr->reset_query(); - - return $this->_db->query("{$sql} FOR UPDATE"); - } - - /** - * Share lock the selected rows in the table. - * - * @return object CI_DB_result - * @example - * $this->Model->find()->where('id', 123) - * $result = $this->Model->sharedLock()->row_array();' - * - */ - public function sharedLock() - { - // Pack query then move it to write DB from read DB for transaction - $sql = $this->_dbr->get_compiled_select(); - $this->_dbr->reset_query(); - - return $this->_db->query("{$sql} LOCK IN SHARE MODE"); - } - - /** - * Without SOFT_DELETED query conditions for next find() - * - * @return $this - * @example - * $this->Model->withTrashed()->find(); - */ - public function withTrashed() - { - $this->_withoutSoftDeletedScope = true; - - return $this; - } - - /** - * Without Global Scopes query conditions for next find() - * - * @return $this - * @example - * $this->Model->withoutGlobalScopes()->find(); - */ - public function withoutGlobalScopes() - { - $this->_withoutGlobalScope = true; - - return $this; - } - - /** - * Without all query conditions for next find() - * That is, with all set of Models for next find() - * - * @return $this - * @example - * $this->Model->withAll()->find(); - */ - public function withAll() - { - // Turn off switches of all featured conditions - $this->withTrashed(); - $this->withoutGlobalScopes(); - - return $this; - } - - /** - * New a Active Record from Model by data - * - * @param array $readProperties - * @param array $selfCondition - * - * @return object ActiveRecord(Model) - */ - public function createActiveRecord($readProperties, $selfCondition) - { - $activeRecord = new static(); - // ORM handling - $activeRecord->_readProperties = $readProperties; - // Primary key condition to ensure single query result - $activeRecord->_selfCondition = $selfCondition; - - return $activeRecord; - } - - /** - * Active Record (ORM) save for insert or update - * - * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. - * - * @return bool Result of CI insert - */ - public function save($runValidation = true) - { - // if (empty($this->_writeProperties)) - // return false; - - // ORM status distinguishing - if (!$this->_selfCondition) { - // Event - if (!$this->beforeSave(true)) { - return false; - } - - $result = $this->insert($this->_writeProperties, $runValidation); - // Change this ActiveRecord to update mode - if ($result) { - // ORM handling - $this->_readProperties = $this->_writeProperties; - $insertID = $this->getLastInsertID(); - $this->_readProperties[$this->primaryKey] = $insertID; - $this->_selfCondition = $insertID; - // Event - $this->afterSave(true, $this->_readProperties); - // Reset properties - $this->_writeProperties = array(); - } - } else { - // Event - if (!$this->beforeSave(false)) { - return false; - } - - $result = ($this->_writeProperties) ? $this->update( - $this->_writeProperties, - $this->_selfCondition, - $runValidation - ) : true; - // Check the primary key is changed - if ($result) { - // Primary key condition to ensure single query result - if (isset($this->_writeProperties[$this->primaryKey])) { - $this->_selfCondition = $this->_writeProperties[$this->primaryKey]; - } - $this->_readProperties = array_merge($this->_readProperties, $this->_writeProperties); - // Event - $this->afterSave(true, $this->_readProperties); - // Reset properties - $this->_writeProperties = array(); - } - } - - return $result; - } - - /** - * This method is called at the beginning of inserting or updating a active record - * - * @param bool $insert whether this method called while inserting a record. - * If `false`, it means the method is called while updating a record. - * - * @return bool whether the insertion or updating should continue. - * If `false`, the insertion or updating will be cancelled. - */ - public function beforeSave($insert) - { - // overriding - return true; - } - - /** - * This method is called at the end of inserting or updating a active record - * - * @param bool $insert whether this method called while inserting a record. - * If `false`, it means the method is called while updating a record. - * @param array $changedAttributes The old values of attributes that had changed and were saved. - * You can use this parameter to take action based on the changes made for example send an email - * when the password had changed or implement audit trail that tracks all the changes. - * `$changedAttributes` gives you the old attribute values while the active record (`$this`) has - * already the new, updated values. - */ - public function afterSave($insert, $changedAttributes) - { - // overriding - } - - /** - * Declares a has-many relation. - * - * @param string $modelName The model class name of the related record - * @param string $foreignKey - * @param string $localKey - * - * @return \CI_DB_query_builder CI_DB_query_builder - */ - public function hasMany($modelName, $foreignKey = null, $localKey = null) - { - return $this->_relationship($modelName, __FUNCTION__, $foreignKey, $localKey); - } - - /** - * Declares a has-many relation. - * - * @param string $modelName The model class name of the related record - * @param string $foreignKey - * @param string $localKey - * - * @return \CI_DB_query_builder CI_DB_query_builder - */ - public function hasOne($modelName, $foreignKey = null, $localKey = null) - { - return $this->_relationship($modelName, __FUNCTION__, $foreignKey, $localKey); - } - - /** - * Base relationship. - * - * @param string $modelName The model class name of the related record - * @param string $relationship - * @param string $foreignKey - * @param string $localKey - * - * @return \CI_DB_query_builder CI_DB_query_builder - */ - protected function _relationship($modelName, $relationship, $foreignKey = null, $localKey = null) - { - /** - * PSR-4 support check - * - * @see https://github.com/yidas/codeigniter-psr4-autoload - */ - if (strpos($modelName, "\\") !== false) { - $model = new $modelName; - } else { - // Original CodeIgniter 3 model loader - get_instance()->load->model($modelName); - // Fix the modelName if it has path - $path = explode('/', $modelName); - $modelName = count($path) > 1 ? end($path) : $modelName; - $model = get_instance()->$modelName; - } - - $libClass = __CLASS__; - - // Check if is using same library - if (!is_subclass_of($model, $libClass)) { - throw new Exception("Model `{$modelName}` does not extend {$libClass}", 500); - } - - // Keys - $foreignKey = ($foreignKey) ? $foreignKey : $this->primaryKey; - $localKey = ($localKey) ? $localKey : $this->primaryKey; - - $query = $model->find() - ->where($foreignKey, $this->$localKey); - - // Inject Model name into query builder for ORM relationships - $query->modelName = $modelName; - // Inject relationship type into query builder for ORM relationships - $query->relationship = $relationship; - - return $query; - } - - /** - * Get relationship property value - * - * @param string $method - * - * @return mixed - */ - protected function _getRelationshipProperty($method) - { - // Cache check - if (isset($this->_relationshipCaches[$method])) { - return $this->_relationshipCaches[$method]; - } - - $query = call_user_func_array([$this, $method], array()); - - // Extract query builder injection property - $modelName = isset($query->modelName) ? $query->modelName : null; - $relationship = isset($query->relationship) ? $query->relationship : null; - - if (!$modelName || !$relationship) { - throw new Exception("ORM relationships error", 500); - } - - /** - * PSR-4 support check - * - * @see https://github.com/yidas/codeigniter-psr4-autoload - */ - if (strpos($modelName, "\\") !== false) { - $model = new $modelName; - } else { - // Original CodeIgniter 3 model loader - get_instance()->load->model($modelName); - $model = get_instance()->$modelName; - } - - // Check return type - $result = ($relationship == 'hasOne') ? $model->findOne(null) : $model->findAll(null); - - // Save cache - $this->_relationshipCaches[$method] = $result; - - return $result; - } - - /** - * Active Record transform to array record - * - * @return array - * @example $record = $activeRecord->toArray(); - */ - public function toArray() - { - return $this->_readProperties; - } - - /** - * Get table schema - * - * @return array Column names - */ - public function getTableSchema() - { - $class = get_class($this); - - // Check ORM Schema cache - if (!isset(self::$_ormCaches[$class])) { - $columns = $this->_dbr->query("SHOW COLUMNS FROM `{$this->table}`;") - ->result_array(); - - // Cache - self::$_ormCaches[$class] = $columns; - } - - return self::$_ormCaches[$class]; - } - - /** - * Index by Key - * - * @param array $array Array data for handling - * @param string $key Array key for index key - * @param bool $obj2Array Object converts to array if is object - * - * @return array Result with indexBy Key - * @example - * $records = $this->Model->findAll(); - * $this->Model->indexBy($records, 'sn'); - */ - public static function indexBy(array &$array, $key = null, $obj2Array = false) - { - // Use model instance's primary key while no given key - $key = ($key) ?: (new static())->primaryKey; - - $tmp = array(); - foreach ($array as $row) { - // Array & Object types support - if (is_object($row) && isset($row->$key)) { - $tmp[$row->$key] = ($obj2Array) ? (array)$row : $row; - } elseif (is_array($row) && isset($row[$key])) { - $tmp[$row[$key]] = $row; - } - } - - return $array = $tmp; - } - - /** - * Encodes special characters into HTML entities. - * - * The [[$this->config->item('charset')]] will be used for encoding. - * - * @param string $content the content to be encoded - * @param bool $doubleEncode whether to encode HTML entities in `$content`. If false, - * HTML entities in `$content` will not be further encoded. - * - * @return string the encoded content - * - * @see http://www.php.net/manual/en/function.htmlspecialchars.php - * @see https://www.yiiframework.com/doc/api/2.0/yii-helpers-basehtml#encode()-detail - */ - public static function htmlEncode($content, $doubleEncode = true) - { - $ci = &get_instance(); - - return htmlspecialchars( - $content, - ENT_QUOTES | ENT_SUBSTITUTE, - $ci->config->item('charset') ? $ci->config->item('charset') : 'UTF-8', - $doubleEncode - ); - } - - /** - * Decodes special HTML entities back to the corresponding characters. - * - * This is the opposite of [[encode()]]. - * - * @param string $content the content to be decoded - * - * @return string the decoded content - * @see htmlEncode() - * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php - * @see https://www.yiiframework.com/doc/api/2.0/yii-helpers-basehtml#decode()-detail - */ - public static function htmlDecode($content) - { - return htmlspecialchars_decode($content, ENT_QUOTES); - } - - /** - * Query Scopes Handler - * - * @return bool Result - */ - protected function _globalScopes() - { - // Events for inheriting - - return true; - } - - /** - * Attributes handle function for each Insert - * - * @param array $attributes - * - * @return array Addon $attributes of pointer - */ - protected function _attrEventBeforeInsert(&$attributes) - { - $this->_formatDate(static::CREATED_AT, $attributes); - - // Trigger UPDATED_AT - if ($this->createdWithUpdated) { - $this->_formatDate(static::UPDATED_AT, $attributes); - } - - return $attributes; - } - - /** - * Attributes handle function for Update - * - * @param array $attributes - * - * @return array Addon $attributes of pointer - */ - protected function _attrEventBeforeUpdate(&$attributes) - { - $this->_formatDate(static::UPDATED_AT, $attributes); - - return $attributes; - } - - /** - * Attributes handle function for Delete - * - * @param array $attributes - * - * @return array Addon $attributes of pointer - */ - protected function _attrEventBeforeDelete(&$attributes) - { - $this->_formatDate(static::DELETED_AT, $attributes); - - return $attributes; - } - - /** - * Finds record(s) by the given condition with a fresh query. - * - * This method is internally called by findOne(), findAll(), update(), delete(), etc. - * The query will be reset to start a new scope if the condition is used. - * - * @param mixed Primary key value or a set of column values. If is null, it would be used for - * previous find() method, which means it would not rebuild find() so it would check and - * protect the SQL statement. - * - * @return \CI_DB_query_builder CI_DB_query_builder - * @internal - * @example - * // find a single customer whose primary key value is 10 - * $this->_findByCondition(10); - * - * // find the customers whose primary key value is 10, 11 or 12. - * $this->_findByCondition([10, 11, 12]); - * - * // find the first customer whose age is 30 and whose status is 1 - * $this->_findByCondition(['age' => 30, 'status' => 1]); - */ - protected function _findByCondition($condition = null) - { - // Reset Query if condition existed - if ($condition !== null) { - $this->_dbr->reset_query(); - $query = $this->find(); - } else { - // Support for previous find(), no need to find() again - $query = $this->_dbr; - } - - // Check condition type - if (is_array($condition)) { - // Check if is numeric array - if (array_keys($condition) === range(0, count($condition) - 1)) { - /* Numeric Array */ - $query->where_in($this->_field($this->primaryKey), $condition); - } else { - /* Associated Array */ - foreach ($condition as $field => $value) { - (is_array($value)) ? $query->where_in($field, $value) : $query->where($field, $value); - } - } - } elseif (is_numeric($condition) || is_string($condition)) { - /* Single Primary Key */ - $query->where($this->_field($this->primaryKey), $condition); - } else { - // Simply Check SQL for no condition such as update/delete - // Warning: This protection just simply check keywords that may not find out for some situations. - $sql = $this->_dbr->get_compiled_select('', false); // No reset query - // Check FROM for table condition - if (stripos($sql, 'from ') === false) { - throw new Exception("You should find() first, or use condition array for update/delete", 400); - } - // No condition situation needs to enable where protection - if (stripos($sql, 'where ') === false) { - throw new Exception( - "You could not update/delete without any condition! Use find()->where('1=1') or condition array at least.", - 400 - ); - } - } - - return $query; - } - - /** - * Format a date for timestamps - * - * @param string Field name - * @param array Attributes - * - * @return array Addon $attributes of pointer - */ - protected function _formatDate($field, &$attributes) - { - if ($this->timestamps && $field) { - switch ($this->dateFormat) { - case 'datetime': - $dateFormat = date("Y-m-d H:i:s"); - break; - - case 'unixtime': - default: - $dateFormat = time(); - break; - } - - $attributes[$field] = $dateFormat; - } - - return $attributes; - } - - /** - * The scope which not been soft deleted - * - * @param bool $skip Skip - * - * @return bool Result - */ - protected function _addSoftDeletedCondition() - { - if ($this->_withoutSoftDeletedScope) { - // Reset SOFT_DELETED switch - $this->_withoutSoftDeletedScope = false; - } elseif (static::SOFT_DELETED && isset($this->softDeletedFalseValue)) { - // Add condition - $this->_dbr->where( - $this->_field(static::SOFT_DELETED), - $this->softDeletedFalseValue - ); - } - - return true; - } - - /** - * The scope which not been soft deleted - * - * @param bool $skip Skip - * - * @return bool Result - */ - protected function _addGlobalScopeCondition() - { - if ($this->_withoutGlobalScope) { - // Reset Global Switch switch - $this->_withoutGlobalScope = false; - } else { - // Default to apply global scopes - $this->_globalScopes(); - } - - return true; - } - - /** - * Standardize field name - * - * @param string $columnName - * - * @return string Standardized column name - */ - protected function _field($columnName) - { - return ($this->alias) ? "`{$this->alias}`.`{$columnName}`" : "`{$this->table}`.`{$columnName}`"; - } - - /** - * Get & load $this->db in CI application - * - * @return object CI $this->db - */ - private function _getDefaultDB() - { - // For ReadDatabase checking Master first - if ($this->_db) { - return $this->_db; - } - - if (!isset($this->db)) { - get_instance()->load->database(); - } - - // No need to set as reference because $this->db is refered to &DB already. - return get_instance()->db; - } - - /** - * ORM set property - * - * @param string $name Property key name - * @param mixed $value - */ - public function __set($name, $value) - { - // Property check option - if ($this->propertyCheck) { - $flag = false; - - // Check if exists - foreach ($this->getTableSchema() as $key => $column) { - if ($name == $column['Field']) { - $flag = true; - } - } - - // No mathc Exception - if (!$flag) { - throw new \Exception("Property `{$name}` does not exist", 500); - } - } - - $this->_writeProperties[$name] = $value; - } - - /** - * ORM get property - * - * @param string $name Property key name - */ - public function __get($name) - { - // ORM property check - if (array_key_exists($name, $this->_writeProperties)) { - return $this->_writeProperties[$name]; - } elseif (array_key_exists($name, $this->_readProperties)) { - return $this->_readProperties[$name]; - } // ORM relationship check - elseif (method_exists($this, $method = $name)) { - return $this->_getRelationshipProperty($method); - } // ORM schema check - else { - // Write cache to read properties of this ORM - foreach ($this->getTableSchema() as $key => $column) { - $this->_readProperties[$column['Field']] = isset($this->_readProperties[$column['Field']]) - ? $this->_readProperties[$column['Field']] - : null; - } - - // Match property again - if (array_key_exists($name, $this->_readProperties)) { - return $this->_readProperties[$name]; - } - - // CI parent::__get() check - if (property_exists(get_instance(), $name)) { - return parent::__get($name); - } - - // Exception - throw new \Exception("Property `{$name}` does not exist", 500); - } - - return null; - } - - /** - * ORM isset property - * - * @param string $name - * - * @return void - */ - public function __isset($name) - { - if (isset($this->_writeProperties[$name])) { - return true; - } elseif (isset($this->_readProperties[$name])) { - return true; - } elseif (method_exists($this, $method = $name)) { - return ($this->_getRelationshipProperty($method)); - } - - return false; - } - - /** - * ORM unset property - * - * @param string $name - * - * @return void - */ - public function __unset($name) - { - unset($this->_writeProperties[$name]); - unset($this->_readProperties[$name]); - } - - /** - * ArrayAccess offsetSet - * - * @param string $offset - * @param mixed $value - * - * @return void - */ - public function offsetSet($offset, $value) - { - return $this->__set($offset, $value); - } - - /** - * ArrayAccess offsetExists - * - * @param string $offset - * - * @return bool Result - */ - public function offsetExists($offset) - { - return $this->__isset($offset); - } - - /** - * ArrayAccess offsetUnset - * - * @param string $offset - * - * @return void - */ - public function offsetUnset($offset) - { - return $this->__unset($offset); - } - - /** - * ArrayAccess offsetGet - * - * @param string $offset - * - * @return mixed Value of property - */ - public function offsetGet($offset) - { - return $this->$offset; - } - } + /** + * Project codeigniter-framework + * Created by PhpStorm + * User: 713uk13m + * Copyright: 713uk13m + * Date: 23/06/2022 + * Time: 01:46 + */ + class HungNG_ORM_Model extends CI_Model implements ArrayAccess + { + /** + * Database Configuration for read-write master + * + * @var object|string|array CI DB ($this->db as default), CI specific group name or CI database config array + */ + protected $database = ""; + + /** + * Database Configuration for read-only slave + * + * @var object|string|array CI DB ($this->db as default), CI specific group name or CI database config array + */ + protected $databaseRead = ""; + + /** + * Table name + * + * @var string + */ + protected $table = ""; + + /** + * Table alias name + * + * @var string + */ + protected $alias = null; + + /** + * Primary key of table + * + * @var string Field name of single column primary key + */ + protected $primaryKey = 'id'; + + /** + * Fillable columns of table + * + * @var array Field names of columns + */ + protected $fillable = array(); + + /** + * Indicates if the model should be timestamped. + * + * @var bool + */ + protected $timestamps = true; + + /** + * Date format for timestamps. + * + * @var string unixtime|datetime + */ + protected $dateFormat = 'datetime'; + + /** + * @string Feild name for created_at, empty is disabled. + */ + const CREATED_AT = 'created_at'; + + /** + * @string Feild name for updated_at, empty is disabled. + */ + const UPDATED_AT = 'updated_at'; + + /** + * CREATED_AT triggers UPDATED_AT. + * + * @var bool + */ + protected $createdWithUpdated = true; + + /** + * @var string Feild name for SOFT_DELETED, empty is disabled. + */ + const SOFT_DELETED = ''; + + /** + * The active value for SOFT_DELETED + * + * @var mixed + */ + protected $softDeletedFalseValue = '0'; + + /** + * The deleted value for SOFT_DELETED + * + * @var mixed + */ + protected $softDeletedTrueValue = '1'; + + /** + * This feature is actvied while having SOFT_DELETED + * + * @var string Feild name for deleted_at, empty is disabled. + */ + const DELETED_AT = ''; + + /** + * Check property schema for write + * + * @var boolean + */ + protected $propertyCheck = false; + + /** + * @var array Validation errors (depends on validator driver) + */ + protected $_errors; + + /** + * @var object database connection for write + */ + protected $_db; + + /** + * @var object database connection for read (Salve) + */ + protected $_dbr; + + /** + * @var object database caches by database key for write + */ + protected static $_dbCaches = array(); + + /** + * @var object database caches by database key for read (Salve) + */ + protected static $_dbrCaches = array(); + + /** + * @var object ORM schema caches by model class namespace + */ + private static $_ormCaches = array(); + + /** + * @var bool SOFT_DELETED one time switch + */ + private $_withoutSoftDeletedScope = false; + + /** + * @var bool Global Scope one time switch + */ + private $_withoutGlobalScope = false; + + /** + * ORM read properties + * + * @var array + */ + private $_readProperties = array(); + + /** + * ORM write properties + * + * @var array + */ + private $_writeProperties = array(); + + /** + * ORM self query + * + * @var string + */ + private $_selfCondition = null; + + /** + * Clean next find one time setting + * + * @var boolean + */ + private $_cleanNextFind = false; + + /** + * Relationship property caches by method name + * + * @var array + */ + private $_relationshipCaches = array(); + + /** + * Constructor + */ + public function __construct() + { + parent::__construct(); + + log_message('info', 'HungNG_ORM_Model Class Initialized'); + + /* Database Connection Setting */ + // Master + if ($this->database) { + if (is_object($this->database)) { + // CI DB Connection + $this->_db = $this->database; + } elseif (is_string($this->database)) { + // Cache Mechanism + if (isset(self::$_dbCaches[$this->database])) { + $this->_db = self::$_dbCaches[$this->database]; + } else { + // CI Database Configuration + $this->_db = get_instance()->load->database($this->database, true); + self::$_dbCaches[$this->database] = $this->_db; + } + } else { + // Config array for each Model + $this->_db = get_instance()->load->database($this->database, true); + } + } else { + // CI Default DB Connection + $this->_db = $this->_getDefaultDB(); + } + // Slave + if ($this->databaseRead) { + if (is_object($this->databaseRead)) { + // CI DB Connection + $this->_dbr = $this->databaseRead; + } elseif (is_string($this->databaseRead)) { + // Cache Mechanism + if (isset(self::$_dbrCaches[$this->databaseRead])) { + $this->_dbr = self::$_dbrCaches[$this->databaseRead]; + } else { + // CI Database Configuration + $this->_dbr = get_instance()->load->database($this->databaseRead, true); + self::$_dbrCaches[$this->databaseRead] = $this->_dbr; + } + } else { + // Config array for each Model + $this->_dbr = get_instance()->load->database($this->databaseRead, true); + } + } else { + // CI Default DB Connection + $this->_dbr = $this->_getDefaultDB(); + } + + /* Table Name Guessing */ + if (!$this->table) { + $this->table = str_replace('_model', '', bear_str_to_lower(get_called_class())); + } + } + + public function __destruct() + { + if ($this->db->conn_id) { + $this->db->close(); + log_message( + 'info', + 'Class "HungNG_ORM_Model" Class Destructed and database disconnected' + ); + } + } + + /** + * Get Master Database Connection + * + * @return object CI &DB + */ + public function getDatabase() + { + return $this->_db; + } + + /** + * Get Slave Database Connection + * + * @return object CI &DB + */ + public function getDatabaseRead() + { + return $this->_dbr; + } + + /** + * Alias of getDatabase() + */ + public function getDB() + { + return $this->getDatabase(); + } + + /** + * Alias of getDatabaseRead() + */ + public function getDBR() + { + return $this->getDatabaseRead(); + } + + /** + * Alias of getDatabaseRead() + */ + public function getBuilder() + { + return $this->getDatabaseRead(); + } + + /** + * Get table name + * + * @return string Table name + */ + public function getTable() + { + return $this->table; + } + + /** + * Alias of getTable() + */ + public function tableName() + { + return $this->getTable(); + } + + /** + * Returns the filter rules for validation. + * + * @return array Filter rules. [[['attr1','attr2'], 'callable'],] + */ + public function filters() + { + return array(); + } + + /** + * Returns the validation rules for attributes. + * + * @see https://www.codeigniter.com/userguide3/libraries/form_validation.html#rule-reference + * @return array validation rules. (CodeIgniter Rule Reference) + */ + public function rules() + { + return array(); + } + + /** + * Performs the data validation with filters + * + * ORM only performs validation for assigned properties. + * + * @param array Data of attributes + * @param boolean Return filtered data + * + * @return boolean Result + * @return mixed Data after filter ($returnData is true) + */ + public function validate($attributes = array(), $returnData = false) + { + // Data fetched by ORM or input + $data = ($attributes) ? $attributes : $this->_writeProperties; + // Filter first + $data = $this->filter($data); + // ORM re-assign properties + $this->_writeProperties = (!$attributes) ? $data : $this->_writeProperties; + // Get validation rules from function setting + $rules = $this->rules(); + + // The ORM update will only collect rules with corresponding modified attributes. + if ($this->_selfCondition) { + $newRules = array(); + foreach ((array)$rules as $key => $rule) { + if (isset($this->_writeProperties[$rule['field']])) { + // Add into new rules for updating + $newRules[] = $rule; + } + } + // Replace with mapping rules + $rules = $newRules; + } + + // Check if has rules + if (empty($rules)) { + return ($returnData) ? $data : true; + } + + // CodeIgniter form_validation doesn't work with empty array data + if (empty($data)) { + return false; + } + + // Load CodeIgniter form_validation library for yidas/model namespace, which has no effect on common one + get_instance()->load->library('form_validation', null, 'yidas_model_form_validation'); + // Get CodeIgniter validator + $validator = get_instance()->yidas_model_form_validation; + $validator->reset_validation(); + $validator->set_data($data); + $validator->set_rules($rules); + // Run Validate + $result = $validator->run(); + + // Result handle + if ($result === false) { + $this->_errors = $validator->error_array(); + + return false; + } else { + return ($returnData) ? $data : true; + } + } + + /** + * Validation - Get error data referenced by last failed Validation + * + * @return array + */ + public function getErrors() + { + return $this->_errors; + } + + /** + * Validation - Reset errors + * + * @return boolean + */ + public function resetErrors() + { + $this->_errors = null; + + return true; + } + + /** + * Filter process + * + * @param array $data Attributes + * + * @return array Filtered data + */ + public function filter($data) + { + // Get filter rules + $filters = $this->filters(); + + // Filter process with setting check + if (!empty($filters) && is_array($filters)) { + foreach ($filters as $key => $filter) { + if (!isset($filter[0])) { + throw new Exception( + "No attributes defined in \$filters from " . get_called_class() . " (" . __CLASS__ . ")", + 500 + ); + } + + if (!isset($filter[1])) { + throw new Exception( + "No function defined in \$filters from " . get_called_class() . " (" . __CLASS__ . ")", 500 + ); + } + + list($attributes, $function) = $filter; + + $attributes = (is_array($attributes)) ? $attributes : [$attributes]; + + // Filter each attribute + foreach ($attributes as $key2 => $attribute) { + if (!isset($data[$attribute])) { + continue; + } + + $data[$attribute] = call_user_func($function, $data[$attribute]); + } + } + } + + return $data; + } + + /** + * Set table alias for next find() + * + * @param string Table alias name + * + * @return $this + */ + public function setAlias($alias) + { + $this->alias = $alias; + + // Turn off cleaner to prevent continuous setting + $this->_cleanNextFind = false; + + return $this; + } + + /** + * Create an existent CI Query Builder instance with Model features for query purpose. + * + * @param boolean $withAll withAll() switch helper + * + * @return \CI_DB_query_builder CI_DB_query_builder + * @example + * $posts = $this->PostModel->find() + * ->where('is_public', '1') + * ->limit(0,25) + * ->order_by('id') + * ->get() + * ->result_array(); + * @example + * // Without all featured conditions for next find() + * $posts = $this->PostModel->find(true) + * ->where('is_deleted', '1') + * ->get() + * ->result_array(); + * // This is equal to withAll() method + * $this->PostModel->withAll()->find(); + * + */ + public function find($withAll = false) + { + $instance = (isset($this)) ? $this : new static; + + // One time setting reset mechanism + if ($instance->_cleanNextFind === true) { + // Reset alias + $instance->setAlias(null); + } else { + // Turn on clean for next find + $instance->_cleanNextFind = true; + } + + // Alias option for FROM + $sqlFrom = ($instance->alias) ? "{$instance->table} AS {$instance->alias}" : $instance->table; + + $instance->_dbr->from($sqlFrom); + + // WithAll helper + if ($withAll === true) { + $instance->withAll(); + } + + // Scope condition + $instance->_addGlobalScopeCondition(); + + // Soft Deleted condition + $instance->_addSoftDeletedCondition(); + + return $instance->_dbr; + } + + /** + * Create an CI Query Builder instance without Model Filters for query purpose. + * + * @return \CI_DB_query_builder CI_DB_query_builder + */ + public function forceFind() + { + return $this->withAll()->find(); + } + + /** + * Return a single active record model instance by a primary key or an array of column values. + * + * @param mixed $condition Refer to _findByCondition() for the explanation of this parameter + * + * @return object ActiveRecord(Model) + * @throws \Exception + * @example + * // Query builder ORM usage + * $this->Model->find()->where('id', 123); + * $this->Model->findOne(); + * @example + * $post = $this->Model->findOne(123); + */ + public static function findOne($condition) + { + if ((isset($this))) { + $instance = $this; + } else { + $instance = new static; + } + + $record = $instance->_findByCondition($condition) + ->limit(1) + ->get()->row_array(); + + // Record check + if (!$record) { + return $record; + } + + return $instance->createActiveRecord($record, $record[$instance->primaryKey]); + } + + /** + * Returns a list of active record models that match the specified primary key value(s) or a set of column values. + * + * @param mixed $condition Refer to _findByCondition() for the explanation + * @param integer|array $limit Limit or [offset, limit] + * + * @return array Set of ActiveRecord(Model)s + * @throws \Exception + * @example + * // Query builder ORM usage + * $this->Model->find()->where_in('id', [3,21,135]); + * $this->Model->findAll(); + * @example + * $post = $this->PostModel->findAll([3,21,135]); + */ + public static function findAll($condition = array(), $limit = null) + { + $instance = (isset($this)) ? $this : new static; + + $query = $instance->_findByCondition($condition); + + // Limit / offset + if ($limit) { + $offset = null; + + if (is_array($limit) && isset($limit[1])) { + // Prevent list() variable effect + $set = $limit; + list($offset, $limit) = $set; + } + + $query = ($limit) ? $query->limit($limit) : $query; + $query = ($offset) ? $query->offset($offset) : $query; + } + + $records = $query->get()->result_array(); + + // Record check + if (!$records) { + return $records; + } + + $set = array(); + // Each ActiveRecord + foreach ((array)$records as $key => $record) { + // Check primary key setting + if (!isset($record[$instance->primaryKey])) { + throw new Exception("Model's primary key not set", 500); + } + // Create an ActiveRecord into collect + $set[] = $instance->createActiveRecord($record, $record[$instance->primaryKey]); + } + + return $set; + } + + /** + * reset an CI Query Builder instance with Model. + * + * @return $this + * @example + * $this->Model->reset()->find(); + */ + public function reset() + { + // Reset query + $this->_db->reset_query(); + $this->_dbr->reset_query(); + + return $this; + } + + /** + * Insert a row with Timestamps feature into the associated database table using the attribute values of this record. + * + * @param array $attributes + * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. + * + * @return boolean Result + * @example + * $result = $this->Model->insert([ + * 'name' => 'Nick Tsai', + * 'email' => 'myintaer@gmail.com', + * ]); + */ + public function insert($attributes, $runValidation = true) + { + // Validation + if ($runValidation && false === $attributes = $this->validate($attributes, true)) { + return false; + } + + $this->_attrEventBeforeInsert($attributes); + + if ($this->fillable) { + $attributes = array_intersect_key($attributes, array_flip($this->fillable)); + } + + return $this->_db->insert($this->table, $attributes); + } + + /** + * Insert a batch of rows with Timestamps feature into the associated database table using the attribute values of this record. + * + * @param array $data The rows to be batch inserted + * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. + * + * @return int Number of rows inserted or FALSE on failure + * @example + * $result = $this->Model->batchInsert([ + * ['name' => 'Nick Tsai', 'email' => 'myintaer@gmail.com'], + * ['name' => 'Yidas', 'email' => 'service@yidas.com'] + * ]); + */ + public function batchInsert($data, $runValidation = true) + { + foreach ($data as $key => &$attributes) { + // Validation + if ($runValidation && false === $attributes = $this->validate($attributes, true)) { + return false; + } + + $this->_attrEventBeforeInsert($attributes); + } + + return $this->_db->insert_batch($this->table, $data); + } + + /** + * Get the insert ID number when performing database inserts. + * + * @param string $name Name of the sequence object from which the ID should be returned. + * + * @return integer Last insert ID + */ + public function getLastInsertID($name = null) + { + return $this->getDB()->insert_id($name); + } + + /** + * Replace a row with Timestamps feature into the associated database table using the attribute values of this record. + * + * @param array $attributes + * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. + * + * @return bool Result + * @example + * $result = $this->Model->replace([ + * 'id' => 1, + * 'name' => 'Nick Tsai', + * 'email' => 'myintaer@gmail.com', + * ]); + */ + public function replace($attributes, $runValidation = true) + { + // Validation + if ($runValidation && false === $attributes = $this->validate($attributes, true)) { + return false; + } + + $this->_attrEventBeforeInsert($attributes); + + return $this->_db->replace($this->table, $attributes); + } + + /** + * Save the changes with Timestamps feature to the selected record(s) into the associated database table. + * + * @param array $attributes + * @param mixed $condition Refer to _findByCondition() for the explanation + * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. + * + * @return bool Result + * + * @example + * $this->Model->update(['status'=>'off'], 123) + * @example + * // Query builder ORM usage + * $this->Model->find()->where('id', 123); + * $this->Model->update(['status'=>'off']); + */ + public function update($attributes, $condition = null, $runValidation = true) + { + // Validation + if ($runValidation && false === $attributes = $this->validate($attributes, true)) { + return false; + } + + // Model Condition + $query = $this->_findByCondition($condition); + + $attributes = $this->_attrEventBeforeUpdate($attributes); + + if ($this->fillable) { + $attributes = array_intersect_key($attributes, array_flip($this->fillable)); + } + + // Pack query then move it to write DB from read DB + $sql = $this->_dbr->set($attributes)->get_compiled_update(); + $this->_dbr->reset_query(); + + return $this->_db->query($sql); + } + + /** + * Update a batch of update queries into combined query strings. + * + * @param array $dataSet [[[Attributes], [Condition]], ] + * @param boolean $withAll withAll() switch helper + * @param integer $maxLenth MySQL max_allowed_packet + * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. + * + * @return integer Count of successful query pack(s) + * @example + * $result = $this->Model->batchUpdate([ + * [['title'=>'A1', 'modified'=>'1'], ['id'=>1]], + * [['title'=>'A2', 'modified'=>'1'], ['id'=>2]], + * ];); + */ + public function batchUpdate(array $dataSet, $withAll = false, $maxLength = null, $runValidation = true) + { + $maxLength = $maxLength ?: 4 * 1024 * 1024; + + $count = 0; + $sqlBatch = ''; + + foreach ($dataSet as $key => &$each) { + // Data format + list($attributes, $condition) = $each; + + // Check attributes + if (!is_array($attributes) || !$attributes) { + continue; + } + + // Validation + if ($runValidation && false === $attributes = $this->validate($attributes, true)) { + continue; + } + + // WithAll helper + if ($withAll === true) { + $this->withAll(); + } + + // Model Condition + $query = $this->_findByCondition($condition); + + $attributes = $this->_attrEventBeforeUpdate($attributes); + + // Pack query then move it to write DB from read DB + $sql = $this->_dbr->set($attributes)->get_compiled_update(); + $this->_dbr->reset_query(); + + // Last batch check: First single query & Max length + // The first single query needs to be sent ahead to prevent the limitation that PDO transaction could not + // use multiple SQL line in one query, but allows if the multi-line query is behind a single query. + if (($count == 0 && $sqlBatch) || bear_str_length($sqlBatch) >= $maxLength) { + // Each batch of query + $result = $this->_db->query($sqlBatch); + $sqlBatch = ""; + $count = ($result) ? $count + 1 : $count; + } + + // Keep Combining query + $sqlBatch .= "{$sql};\n"; + } + + // Last batch of query + $result = $this->_db->query($sqlBatch); + + return ($result) ? $count + 1 : $count; + } + + /** + * Delete the selected record(s) with Timestamps feature into the associated database table. + * + * @param mixed $condition Refer to _findByCondition() for the explanation + * @param boolean $forceDelete Force to hard delete + * @param array $attributes Extended attributes for Soft Delete Mode + * + * @return bool Result + * + * @example + * $this->Model->delete(123); + * @example + * // Query builder ORM usage + * $this->Model->find()->where('id', 123); + * $this->Model->delete(); + * @example + * // Force delete for SOFT_DELETED mode + * $this->Model->delete(123, true); + */ + public function delete($condition = null, $forceDelete = false, $attributes = array()) + { + // Check is Active Record + if ($this->_readProperties) { + // Reset condition and find single by self condition + $this->reset(); + $condition = $this->_selfCondition; + } + + // Model Condition by $forceDelete switch + $query = ($forceDelete) + ? $this->withTrashed()->_findByCondition($condition) + : $this->_findByCondition($condition); + + /* Soft Delete Mode */ + if (static::SOFT_DELETED + && isset($this->softDeletedTrueValue) + && !$forceDelete) { + // Mark the records as deleted + $attributes[static::SOFT_DELETED] = $this->softDeletedTrueValue; + + $attributes = $this->_attrEventBeforeDelete($attributes); + + // Pack query then move it to write DB from read DB + $sql = $this->_dbr->set($attributes)->get_compiled_update(); + $this->_dbr->reset_query(); + } else { + /* Hard Delete */ + // Pack query then move it to write DB from read DB + $sql = $this->_dbr->get_compiled_delete(); + $this->_dbr->reset_query(); + } + + return $this->_db->query($sql); + } + + /** + * Force Delete the selected record(s) with Timestamps feature into the associated database table. + * + * @param mixed $condition Refer to _findByCondition() for the explanation + * + * @return mixed CI delete result of DB Query Builder + * + * @example + * $this->Model->forceDelete(123) + * @example + * // Query builder ORM usage + * $this->Model->find()->where('id', 123); + * $this->Model->forceDelete(); + */ + public function forceDelete($condition = null) + { + return $this->delete($condition, true); + } + + /** + * Get the number of affected rows when doing “write” type queries (insert, update, etc.). + * + * @return integer Last insert ID + */ + public function getAffectedRows() + { + return $this->getDB()->affected_rows(); + } + + /** + * Restore SOFT_DELETED field value to the selected record(s) into the associated database table. + * + * @param mixed $condition Refer to _findByCondition() for the explanation + * + * @return bool Result + * + * @example + * $this->Model->restore(123) + * @example + * // Query builder ORM usage + * $this->Model->withTrashed()->find()->where('id', 123); + * $this->Model->restore(); + */ + public function restore($condition = null) + { + // Model Condition with Trashed + $query = $this->withTrashed()->_findByCondition($condition); + + /* Soft Delete Mode */ + if (static::SOFT_DELETED + && isset($this->softDeletedFalseValue)) { + // Mark the records as deleted + $attributes[static::SOFT_DELETED] = $this->softDeletedFalseValue; + + return $query->update($this->table, $attributes); + } else { + return false; + } + } + + /** + * Get count from query + * + * @param boolean Reset query conditions + * + * @return integer + */ + public function count($resetQuery = true) + { + return $this->getDBR()->count_all_results('', $resetQuery); + } + + /** + * Lock the selected rows in the table for updating. + * + * sharedLock locks only for write, lockForUpdate also prevents them from being selected + * + * @return object CI_DB_result + * @example + * // This transaction block will lock selected rows for next same selected + * // rows with `FOR UPDATE` lock: + * $this->Model->getDB()->trans_start(); + * $this->Model->find()->where('id', 123) + * $result = $this->Model->lockForUpdate()->row_array(); + * $this->Model->getDB()->trans_complete(); + * + * @example + * $this->Model->find()->where('id', 123) + * $result = $this->Model->lockForUpdate()->row_array(); + */ + public function lockForUpdate() + { + // Pack query then move it to write DB from read DB for transaction + $sql = $this->_dbr->get_compiled_select(); + $this->_dbr->reset_query(); + + return $this->_db->query("{$sql} FOR UPDATE"); + } + + /** + * Share lock the selected rows in the table. + * + * @return object CI_DB_result + * @example + * $this->Model->find()->where('id', 123) + * $result = $this->Model->sharedLock()->row_array();' + * + */ + public function sharedLock() + { + // Pack query then move it to write DB from read DB for transaction + $sql = $this->_dbr->get_compiled_select(); + $this->_dbr->reset_query(); + + return $this->_db->query("{$sql} LOCK IN SHARE MODE"); + } + + /** + * Without SOFT_DELETED query conditions for next find() + * + * @return $this + * @example + * $this->Model->withTrashed()->find(); + */ + public function withTrashed() + { + $this->_withoutSoftDeletedScope = true; + + return $this; + } + + /** + * Without Global Scopes query conditions for next find() + * + * @return $this + * @example + * $this->Model->withoutGlobalScopes()->find(); + */ + public function withoutGlobalScopes() + { + $this->_withoutGlobalScope = true; + + return $this; + } + + /** + * Without all query conditions for next find() + * That is, with all set of Models for next find() + * + * @return $this + * @example + * $this->Model->withAll()->find(); + */ + public function withAll() + { + // Turn off switches of all featured conditions + $this->withTrashed(); + $this->withoutGlobalScopes(); + + return $this; + } + + /** + * New a Active Record from Model by data + * + * @param array $readProperties + * @param array $selfCondition + * + * @return object ActiveRecord(Model) + */ + public function createActiveRecord($readProperties, $selfCondition) + { + $activeRecord = new static(); + // ORM handling + $activeRecord->_readProperties = $readProperties; + // Primary key condition to ensure single query result + $activeRecord->_selfCondition = $selfCondition; + + return $activeRecord; + } + + /** + * Active Record (ORM) save for insert or update + * + * @param boolean $runValidation Whether to perform validation (calling validate()) before manipulate the record. + * + * @return bool Result of CI insert + */ + public function save($runValidation = true) + { + // if (empty($this->_writeProperties)) + // return false; + + // ORM status distinguishing + if (!$this->_selfCondition) { + // Event + if (!$this->beforeSave(true)) { + return false; + } + + $result = $this->insert($this->_writeProperties, $runValidation); + // Change this ActiveRecord to update mode + if ($result) { + // ORM handling + $this->_readProperties = $this->_writeProperties; + $insertID = $this->getLastInsertID(); + $this->_readProperties[$this->primaryKey] = $insertID; + $this->_selfCondition = $insertID; + // Event + $this->afterSave(true, $this->_readProperties); + // Reset properties + $this->_writeProperties = array(); + } + } else { + // Event + if (!$this->beforeSave(false)) { + return false; + } + + $result = ($this->_writeProperties) ? $this->update( + $this->_writeProperties, + $this->_selfCondition, + $runValidation + ) : true; + // Check the primary key is changed + if ($result) { + // Primary key condition to ensure single query result + if (isset($this->_writeProperties[$this->primaryKey])) { + $this->_selfCondition = $this->_writeProperties[$this->primaryKey]; + } + $this->_readProperties = array_merge($this->_readProperties, $this->_writeProperties); + // Event + $this->afterSave(true, $this->_readProperties); + // Reset properties + $this->_writeProperties = array(); + } + } + + return $result; + } + + /** + * This method is called at the beginning of inserting or updating a active record + * + * @param bool $insert whether this method called while inserting a record. + * If `false`, it means the method is called while updating a record. + * + * @return bool whether the insertion or updating should continue. + * If `false`, the insertion or updating will be cancelled. + */ + public function beforeSave($insert) + { + // overriding + return true; + } + + /** + * This method is called at the end of inserting or updating a active record + * + * @param bool $insert whether this method called while inserting a record. + * If `false`, it means the method is called while updating a record. + * @param array $changedAttributes The old values of attributes that had changed and were saved. + * You can use this parameter to take action based on the changes made for example send an email + * when the password had changed or implement audit trail that tracks all the changes. + * `$changedAttributes` gives you the old attribute values while the active record (`$this`) has + * already the new, updated values. + */ + public function afterSave($insert, $changedAttributes) + { + // overriding + } + + /** + * Declares a has-many relation. + * + * @param string $modelName The model class name of the related record + * @param string $foreignKey + * @param string $localKey + * + * @return \CI_DB_query_builder CI_DB_query_builder + */ + public function hasMany($modelName, $foreignKey = null, $localKey = null) + { + return $this->_relationship($modelName, __FUNCTION__, $foreignKey, $localKey); + } + + /** + * Declares a has-many relation. + * + * @param string $modelName The model class name of the related record + * @param string $foreignKey + * @param string $localKey + * + * @return \CI_DB_query_builder CI_DB_query_builder + */ + public function hasOne($modelName, $foreignKey = null, $localKey = null) + { + return $this->_relationship($modelName, __FUNCTION__, $foreignKey, $localKey); + } + + /** + * Base relationship. + * + * @param string $modelName The model class name of the related record + * @param string $relationship + * @param string $foreignKey + * @param string $localKey + * + * @return \CI_DB_query_builder CI_DB_query_builder + */ + protected function _relationship($modelName, $relationship, $foreignKey = null, $localKey = null) + { + /** + * PSR-4 support check + * + * @see https://github.com/yidas/codeigniter-psr4-autoload + */ + if (strpos($modelName, "\\") !== false) { + $model = new $modelName; + } else { + // Original CodeIgniter 3 model loader + get_instance()->load->model($modelName); + // Fix the modelName if it has path + $path = explode('/', $modelName); + $modelName = count($path) > 1 ? end($path) : $modelName; + $model = get_instance()->$modelName; + } + + $libClass = __CLASS__; + + // Check if is using same library + if (!is_subclass_of($model, $libClass)) { + throw new Exception("Model `{$modelName}` does not extend {$libClass}", 500); + } + + // Keys + $foreignKey = ($foreignKey) ? $foreignKey : $this->primaryKey; + $localKey = ($localKey) ? $localKey : $this->primaryKey; + + $query = $model->find() + ->where($foreignKey, $this->$localKey); + + // Inject Model name into query builder for ORM relationships + $query->modelName = $modelName; + // Inject relationship type into query builder for ORM relationships + $query->relationship = $relationship; + + return $query; + } + + /** + * Get relationship property value + * + * @param string $method + * + * @return mixed + */ + protected function _getRelationshipProperty($method) + { + // Cache check + if (isset($this->_relationshipCaches[$method])) { + return $this->_relationshipCaches[$method]; + } + + $query = call_user_func_array([$this, $method], array()); + + // Extract query builder injection property + $modelName = isset($query->modelName) ? $query->modelName : null; + $relationship = isset($query->relationship) ? $query->relationship : null; + + if (!$modelName || !$relationship) { + throw new Exception("ORM relationships error", 500); + } + + /** + * PSR-4 support check + * + * @see https://github.com/yidas/codeigniter-psr4-autoload + */ + if (strpos($modelName, "\\") !== false) { + $model = new $modelName; + } else { + // Original CodeIgniter 3 model loader + get_instance()->load->model($modelName); + $model = get_instance()->$modelName; + } + + // Check return type + $result = ($relationship == 'hasOne') ? $model->findOne(null) : $model->findAll(null); + + // Save cache + $this->_relationshipCaches[$method] = $result; + + return $result; + } + + /** + * Active Record transform to array record + * + * @return array + * @example $record = $activeRecord->toArray(); + */ + public function toArray() + { + return $this->_readProperties; + } + + /** + * Get table schema + * + * @return array Column names + */ + public function getTableSchema() + { + $class = get_class($this); + + // Check ORM Schema cache + if (!isset(self::$_ormCaches[$class])) { + $columns = $this->_dbr->query("SHOW COLUMNS FROM `{$this->table}`;") + ->result_array(); + + // Cache + self::$_ormCaches[$class] = $columns; + } + + return self::$_ormCaches[$class]; + } + + /** + * Index by Key + * + * @param array $array Array data for handling + * @param string $key Array key for index key + * @param bool $obj2Array Object converts to array if is object + * + * @return array Result with indexBy Key + * @example + * $records = $this->Model->findAll(); + * $this->Model->indexBy($records, 'sn'); + */ + public static function indexBy(array &$array, $key = null, $obj2Array = false) + { + // Use model instance's primary key while no given key + $key = ($key) ?: (new static())->primaryKey; + + $tmp = array(); + foreach ($array as $row) { + // Array & Object types support + if (is_object($row) && isset($row->$key)) { + $tmp[$row->$key] = ($obj2Array) ? (array)$row : $row; + } elseif (is_array($row) && isset($row[$key])) { + $tmp[$row[$key]] = $row; + } + } + + return $array = $tmp; + } + + /** + * Encodes special characters into HTML entities. + * + * The [[$this->config->item('charset')]] will be used for encoding. + * + * @param string $content the content to be encoded + * @param bool $doubleEncode whether to encode HTML entities in `$content`. If false, + * HTML entities in `$content` will not be further encoded. + * + * @return string the encoded content + * + * @see http://www.php.net/manual/en/function.htmlspecialchars.php + * @see https://www.yiiframework.com/doc/api/2.0/yii-helpers-basehtml#encode()-detail + */ + public static function htmlEncode($content, $doubleEncode = true) + { + $ci = &get_instance(); + + return htmlspecialchars( + $content, + ENT_QUOTES | ENT_SUBSTITUTE, + $ci->config->item('charset') ? $ci->config->item('charset') : 'UTF-8', + $doubleEncode + ); + } + + /** + * Decodes special HTML entities back to the corresponding characters. + * + * This is the opposite of [[encode()]]. + * + * @param string $content the content to be decoded + * + * @return string the decoded content + * @see htmlEncode() + * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php + * @see https://www.yiiframework.com/doc/api/2.0/yii-helpers-basehtml#decode()-detail + */ + public static function htmlDecode($content) + { + return htmlspecialchars_decode($content, ENT_QUOTES); + } + + /** + * Query Scopes Handler + * + * @return bool Result + */ + protected function _globalScopes() + { + // Events for inheriting + + return true; + } + + /** + * Attributes handle function for each Insert + * + * @param array $attributes + * + * @return array Addon $attributes of pointer + */ + protected function _attrEventBeforeInsert(&$attributes) + { + $this->_formatDate(static::CREATED_AT, $attributes); + + // Trigger UPDATED_AT + if ($this->createdWithUpdated) { + $this->_formatDate(static::UPDATED_AT, $attributes); + } + + return $attributes; + } + + /** + * Attributes handle function for Update + * + * @param array $attributes + * + * @return array Addon $attributes of pointer + */ + protected function _attrEventBeforeUpdate(&$attributes) + { + $this->_formatDate(static::UPDATED_AT, $attributes); + + return $attributes; + } + + /** + * Attributes handle function for Delete + * + * @param array $attributes + * + * @return array Addon $attributes of pointer + */ + protected function _attrEventBeforeDelete(&$attributes) + { + $this->_formatDate(static::DELETED_AT, $attributes); + + return $attributes; + } + + /** + * Finds record(s) by the given condition with a fresh query. + * + * This method is internally called by findOne(), findAll(), update(), delete(), etc. + * The query will be reset to start a new scope if the condition is used. + * + * @param mixed Primary key value or a set of column values. If is null, it would be used for + * previous find() method, which means it would not rebuild find() so it would check and + * protect the SQL statement. + * + * @return \CI_DB_query_builder CI_DB_query_builder + * @internal + * @example + * // find a single customer whose primary key value is 10 + * $this->_findByCondition(10); + * + * // find the customers whose primary key value is 10, 11 or 12. + * $this->_findByCondition([10, 11, 12]); + * + * // find the first customer whose age is 30 and whose status is 1 + * $this->_findByCondition(['age' => 30, 'status' => 1]); + */ + protected function _findByCondition($condition = null) + { + // Reset Query if condition existed + if ($condition !== null) { + $this->_dbr->reset_query(); + $query = $this->find(); + } else { + // Support for previous find(), no need to find() again + $query = $this->_dbr; + } + + // Check condition type + if (is_array($condition)) { + // Check if is numeric array + if (array_keys($condition) === range(0, count($condition) - 1)) { + /* Numeric Array */ + $query->where_in($this->_field($this->primaryKey), $condition); + } else { + /* Associated Array */ + foreach ($condition as $field => $value) { + (is_array($value)) ? $query->where_in($field, $value) : $query->where($field, $value); + } + } + } elseif (is_numeric($condition) || is_string($condition)) { + /* Single Primary Key */ + $query->where($this->_field($this->primaryKey), $condition); + } else { + // Simply Check SQL for no condition such as update/delete + // Warning: This protection just simply check keywords that may not find out for some situations. + $sql = $this->_dbr->get_compiled_select('', false); // No reset query + // Check FROM for table condition + if (stripos($sql, 'from ') === false) { + throw new Exception("You should find() first, or use condition array for update/delete", 400); + } + // No condition situation needs to enable where protection + if (stripos($sql, 'where ') === false) { + throw new Exception( + "You could not update/delete without any condition! Use find()->where('1=1') or condition array at least.", + 400 + ); + } + } + + return $query; + } + + /** + * Format a date for timestamps + * + * @param string Field name + * @param array Attributes + * + * @return array Addon $attributes of pointer + */ + protected function _formatDate($field, &$attributes) + { + if ($this->timestamps && $field) { + switch ($this->dateFormat) { + case 'datetime': + $dateFormat = date("Y-m-d H:i:s"); + break; + + case 'unixtime': + default: + $dateFormat = time(); + break; + } + + $attributes[$field] = $dateFormat; + } + + return $attributes; + } + + /** + * The scope which not been soft deleted + * + * @param bool $skip Skip + * + * @return bool Result + */ + protected function _addSoftDeletedCondition() + { + if ($this->_withoutSoftDeletedScope) { + // Reset SOFT_DELETED switch + $this->_withoutSoftDeletedScope = false; + } elseif (static::SOFT_DELETED && isset($this->softDeletedFalseValue)) { + // Add condition + $this->_dbr->where( + $this->_field(static::SOFT_DELETED), + $this->softDeletedFalseValue + ); + } + + return true; + } + + /** + * The scope which not been soft deleted + * + * @param bool $skip Skip + * + * @return bool Result + */ + protected function _addGlobalScopeCondition() + { + if ($this->_withoutGlobalScope) { + // Reset Global Switch switch + $this->_withoutGlobalScope = false; + } else { + // Default to apply global scopes + $this->_globalScopes(); + } + + return true; + } + + /** + * Standardize field name + * + * @param string $columnName + * + * @return string Standardized column name + */ + protected function _field($columnName) + { + return ($this->alias) ? "`{$this->alias}`.`{$columnName}`" : "`{$this->table}`.`{$columnName}`"; + } + + /** + * Get & load $this->db in CI application + * + * @return object CI $this->db + */ + private function _getDefaultDB() + { + // For ReadDatabase checking Master first + if ($this->_db) { + return $this->_db; + } + + if (!isset($this->db)) { + get_instance()->load->database(); + } + + // No need to set as reference because $this->db is refered to &DB already. + return get_instance()->db; + } + + /** + * ORM set property + * + * @param string $name Property key name + * @param mixed $value + */ + public function __set($name, $value) + { + // Property check option + if ($this->propertyCheck) { + $flag = false; + + // Check if exists + foreach ($this->getTableSchema() as $key => $column) { + if ($name == $column['Field']) { + $flag = true; + } + } + + // No mathc Exception + if (!$flag) { + throw new \Exception("Property `{$name}` does not exist", 500); + } + } + + $this->_writeProperties[$name] = $value; + } + + /** + * ORM get property + * + * @param string $name Property key name + */ + public function __get($name) + { + // ORM property check + if (array_key_exists($name, $this->_writeProperties)) { + return $this->_writeProperties[$name]; + } elseif (array_key_exists($name, $this->_readProperties)) { + return $this->_readProperties[$name]; + } // ORM relationship check + elseif (method_exists($this, $method = $name)) { + return $this->_getRelationshipProperty($method); + } // ORM schema check + else { + // Write cache to read properties of this ORM + foreach ($this->getTableSchema() as $key => $column) { + $this->_readProperties[$column['Field']] = isset($this->_readProperties[$column['Field']]) + ? $this->_readProperties[$column['Field']] + : null; + } + + // Match property again + if (array_key_exists($name, $this->_readProperties)) { + return $this->_readProperties[$name]; + } + + // CI parent::__get() check + if (property_exists(get_instance(), $name)) { + return parent::__get($name); + } + + // Exception + throw new \Exception("Property `{$name}` does not exist", 500); + } + + return null; + } + + /** + * ORM isset property + * + * @param string $name + * + * @return void + */ + public function __isset($name) + { + if (isset($this->_writeProperties[$name])) { + return true; + } elseif (isset($this->_readProperties[$name])) { + return true; + } elseif (method_exists($this, $method = $name)) { + return ($this->_getRelationshipProperty($method)); + } + + return false; + } + + /** + * ORM unset property + * + * @param string $name + * + * @return void + */ + public function __unset($name) + { + unset($this->_writeProperties[$name]); + unset($this->_readProperties[$name]); + } + + /** + * ArrayAccess offsetSet + * + * @param string $offset + * @param mixed $value + * + * @return void + */ + public function offsetSet($offset, $value) + { + return $this->__set($offset, $value); + } + + /** + * ArrayAccess offsetExists + * + * @param string $offset + * + * @return bool Result + */ + public function offsetExists($offset) + { + return $this->__isset($offset); + } + + /** + * ArrayAccess offsetUnset + * + * @param string $offset + * + * @return void + */ + public function offsetUnset($offset) + { + return $this->__unset($offset); + } + + /** + * ArrayAccess offsetGet + * + * @param string $offset + * + * @return mixed Value of property + */ + public function offsetGet($offset) + { + return $this->$offset; + } + } }