Files
admin_hura_8/inc/Hura8/System/Model/aEntityBaseModel.php

429 lines
14 KiB
PHP
Raw Normal View History

2024-01-29 10:39:53 +07:00
<?php
namespace Hura8\System\Model;
use Hura8\Database\iConnectDB;
use Hura8\Database\MysqlValue;
use Hura8\Interfaces\iSearch;
abstract class aEntityBaseModel
{
/* @var iConnectDB $db */
protected $db;
protected $entity_type = '';
protected $tb_entity = "";
protected $allow_richtext_fields = []; // only table's column fields in this list allowed to retain html tags, else strip all
protected $base_filter_condition_keys = [
'q' => '', // keyword search
'q_options' => [
'field_filters' => [],
'fulltext_fields' => ['keywords'],
'limit_result' => 2000
], // q_options as in iSearch->find($keyword, array $field_filters = [], array $fulltext_fields = ["keywords"], $limit_result = 2000)
'featured' => '', // 1|-1
'status' => '', // 1|-1
'excluded_ids' => null, // [id1, id2, ...] or null
'included_ids' => null, // [id1, id2, ...] or null
];
/* @var ?iSearch $iSearchModel */
protected $iSearchModel;
public function __construct(
$entity_type,
$tb_entity = "",
$iSearchModel = null,
$allow_richtext_fields = []
) {
$this->entity_type = $entity_type;
$this->db = get_db('', ENABLE_DB_DEBUG);
$this->iSearchModel = $iSearchModel;
$this->allow_richtext_fields = $allow_richtext_fields;
if($tb_entity) {
$this->tb_entity = $tb_entity;
}else{
//auto infer
$tb_name = str_replace("-", "_", preg_replace("/[^a-z0-9_\-]/i", "", $entity_type));
$this->tb_entity = "tb_".strtolower($tb_name);
}
}
/**
* @description to be extended by extending class
* besides the $base_filter_condition_keys which allows to find items in ::getList($condition)
* example:
[
'category' => array[],
'brand' => array[],
]
* @return array
*/
abstract protected function extendedFilterOptions() : array ;
/**
* @description utility to get all possible entity filter during module development
* @return array
*/
public function getAllowedFilterConditionKeys(): array
{
return array_merge(
$this->extendedFilterOptions(),
$this->base_filter_condition_keys
);
}
public function setRichtextFields(array $allow_richtext_fields = []) {
$this->allow_richtext_fields = $allow_richtext_fields;
}
/**
* @description
* @param array $filter_condition
* @return array|null array($string_where, $bind_types, $bind_values );
*/
protected function _buildQueryConditionExtend(array $filter_condition): ?array {
return [];
}
/**
* @param string $sort_by
* @return string
*/
protected function _buildQueryOrderBy(string $sort_by = "new") {
return " `id` DESC ";
}
/**
* @param array $item_info
* @return array
*/
protected function formatItemInList(array $item_info) : array {
return $item_info;
}
/**
* @param array $item_info
* @return array|null
*/
protected function formatItemInfo(array $item_info) : ?array {
return $item_info;
}
public function getEntityType() : string {
return $this->entity_type;
}
/**
* @description prevent xss attack by stripping all html tags from un-permitted fields (which mostly are)
* - only fields in ::allow_richtext_fields can keep html tags. Example: description
* - if value is array (example data=['title' => '', 'description' => html tags ]) ...
then to keep `description`, both `data` and `description` must be in the ::allow_richtext_fields
because this method checks on array value recursively
* @param array $input_info
* @return array
*/
protected function cleanRichtextFields(array $input_info) : array {
$cleaned_info = [];
foreach ($input_info as $key => $value) {
if($value instanceof MysqlValue) {
$cleaned_info[$key] = $value;
} else {
if(in_array($key, $this->allow_richtext_fields)) {
$cleaned_info[$key] = (is_array($value)) ? $this->cleanRichtextFields($value) : $value;
}else{
$cleaned_info[$key] = (is_array($value)) ? $this->cleanRichtextFields($value) : strip_tags($value);
}
}
}
return $cleaned_info;
}
public function getListByIds(array $list_id, array $condition = array()) : array
{
if(!sizeof($list_id)) {
return [];
}
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($list_id, 'int');
$where_clause = "";
$bind_values = $list_id;
if(isset($condition['status'])) {
$where_clause .= " AND `status` = ? ";
$bind_types[] = 'd';
$bind_values[] = intval($condition['status']);
}
$query = $this->db->runQuery(
" SELECT * FROM ".$this->tb_entity." WHERE `id` IN (".$parameterized_ids.") ".$where_clause,
$bind_types,
$bind_values
);
$item_list = [];
foreach ($this->db->fetchAll($query) as $item) {
$item_list[$item['id']] = $this->formatItemInList($item);
}
return $item_list;
}
public function getInfo($id) : ?array
{
$query = $this->db->runQuery("SELECT * FROM `".$this->tb_entity."` WHERE `id` = ? LIMIT 1 ", ['d'], [$id]) ;
if( $item_info = $this->db->fetchAssoc($query)){
return $this->formatItemInfo($item_info);
}
return null;
}
/**
* @description utility to inspect the actual filters which will be used in getList by Model
* @param array $condition
* @return array
*/
public function getQueryCondition(array $condition) : array
{
return $this->_buildQueryCondition($condition);
}
public function getTotal(array $condition) : int
{
list( $where_clause, $bind_types, $bind_values) = $this->_buildQueryCondition($condition);
$query = $this->db->runQuery(
" SELECT COUNT(*) as total FROM `".$this->tb_entity."` WHERE 1 " . $where_clause,
$bind_types, $bind_values
);
$total = 0;
if ($rs = $this->db->fetchAssoc($query)) {
$total = $rs['total'];
}
return $total;
}
public function getList(array $condition) : array
{
list( $where_clause, $bind_types, $bind_values) = $this->_buildQueryCondition($condition);
//debug_var([$where_clause, $bind_types, $bind_values]);
$numPerPage = (isset($condition['numPerPage']) && $condition['numPerPage'] > 0 ) ? intval($condition['numPerPage']) : 20 ;
$page = (isset($condition['page']) && $condition['page'] > 0 ) ? intval($condition['page']) : 1 ;
$sort_by = (isset($condition['sort_by']) && $condition['sort_by']) ? $condition['sort_by'] : 'new' ;
$order_by = $this->_buildQueryOrderBy($sort_by);
$query = $this->db->runQuery(
"SELECT * FROM ".$this->tb_entity." WHERE 1 ". $where_clause . "
ORDER BY ".$order_by."
LIMIT ".(($page-1) * $numPerPage).", ".$numPerPage ,
$bind_types,
$bind_values
) ;
$item_list = array();
$counter = ($page-1) * $numPerPage;
foreach ( $this->db->fetchAll($query) as $item_info ) {
$counter += 1;
$item = $item_info;
$item['counter'] = $counter;
$item_list[] = $this->formatItemInList($item);
}
return $item_list;
}
//to avoid duplicate codes, extended class should implement this method for extra filter keys
// which are not in the base filter ::_buildQueryConditionBase
protected function _buildQueryCondition(array $filter_condition)
{
// these keys are for pagination and ordering list, which are commonly included in the $filter_condition
$excluded_keys = [
'numPerPage', 'page', 'sort_by'
];
$base_filter_conditions = [];
$extend_filter_conditions = [];
foreach ($filter_condition as $key => $value) {
if(in_array($key, $excluded_keys)) {
continue;
}
if(array_key_exists($key, $this->base_filter_condition_keys) ) {
$base_filter_conditions[$key] = $value;
}else{
$extend_filter_conditions[$key] = $value;
}
}
list($base_where, $base_bind_types, $base_bind_values) = $this->_buildQueryConditionBase($base_filter_conditions);
list(
$extend_where,
$extend_bind_types,
$extend_bind_values
) = $this->_buildQueryConditionExtend($extend_filter_conditions) ?: [ '', null, null];
if($extend_where) {
return array(
join(" ", [$base_where, $extend_where]),
array_merge($base_bind_types, $extend_bind_types),
array_merge($base_bind_values, $extend_bind_values),
);
}
return array(
$base_where,
$base_bind_types,
$base_bind_values
);
}
protected function _buildQueryConditionBase(array $filter_conditions)
{
/*
$filter_conditions = [
'q' => '', // keyword search
'q_options' => [
'field_filters' => [],
'fulltext_fields' => ['keywords'],
'limit_result' => 2000
], // q_options as in iSearch->find($keyword, array $field_filters = [], array $fulltext_fields = ["keywords"], $limit_result = 2000)
'featured' => '', // 1|-1
'status' => '', // 1|-1
'excluded_ids' => [], // [id1, id2, ...] or null
'included_ids' => [], // [id1, id2, ...] or null
];
*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
//Tim kiem theo tu khoa
if(isset($filter_conditions["q"]) && $filter_conditions["q"] && $this->iSearchModel ){
$q_options = $filter_conditions["q_options"] ?? [];
$field_filters = $q_options['field_filters'] ?? [];
$fulltext_fields = $q_options['fulltext_fields'] ?? [];
$limit_result = $q_options['limit_result'] ?? 2000;
$match_result = $this->iSearchModel->find($filter_conditions["q"], $field_filters, $fulltext_fields, $limit_result);
list($parameterized_ids, $sub_bind_types) = create_bind_sql_parameter_from_value_list($match_result, 'int');
if($parameterized_ids) {
$catCondition[] = " AND `id` IN (".$parameterized_ids.") ";
$bind_types = array_merge($bind_types, $sub_bind_types);
$bind_values = array_merge($bind_values, $match_result);
}else{
$catCondition[] = " AND `id` = -1 ";
}
}
//loc theo featured
if(isset($filter_conditions["featured"]) && $filter_conditions["featured"]) {
$catCondition[] = " AND `is_featured` = ? ";
$bind_types[] = 'd';
$bind_values[] = ($filter_conditions["featured"] == 1) ? 1 : 0;
}
//loc theo status
if(isset($filter_conditions["status"]) && $filter_conditions["status"]) {
$catCondition[] = " AND `status` = ? ";
$bind_types[] = 'd';
$bind_values[] = ($filter_conditions["status"] == 1) ? 1 : 0;
}
// excluded_ids
if(isset($filter_conditions['excluded_ids']) && is_array($filter_conditions['excluded_ids']) ) {
list($parameterized_ids, $sub_bind_types) = create_bind_sql_parameter_from_value_list($filter_conditions['excluded_ids'], 'int');
if($parameterized_ids) {
$catCondition[] = " AND `id` NOT IN (".$parameterized_ids.") ";
$bind_types = array_merge($bind_types, $sub_bind_types);
$bind_values = array_merge($bind_values, $filter_conditions['excluded_ids']);
}
}
// included_ids
if(isset($filter_conditions['included_ids']) && is_array($filter_conditions['included_ids']) ) {
list($parameterized_ids, $sub_bind_types) = create_bind_sql_parameter_from_value_list($filter_conditions['included_ids'], 'int');
if($parameterized_ids) {
$catCondition[] = " AND `id` IN (".$parameterized_ids.") ";
$bind_types = array_merge($bind_types, $sub_bind_types);
$bind_values = array_merge($bind_values, $filter_conditions['included_ids']);
}else{
$catCondition[] = " AND `id` = -1 ";
}
}
return array(
join(" ", $catCondition),
$bind_types,
$bind_values
);
}
// get empty/default item for form
public function getEmptyInfo($addition_field_value = []) : array
{
$table_info = $this->db->getTableInfo($this->tb_entity);
$empty_info = [];
foreach ($table_info as $field => $field_info) {
$empty_info[$field] = ( in_array($field_info['DATA_TYPE'], ['int', 'float', 'mediumint', 'smallint', 'tinyint']) ) ? 0 : '' ;
}
if(sizeof($addition_field_value)) {
return array_merge($empty_info, $addition_field_value);
}
return $empty_info;
}
}