342 lines
12 KiB
PHP
342 lines
12 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace Hura8\System\Model;
|
||
|
|
|
||
|
|
use Hura8\Database\iConnectDB;
|
||
|
|
use Hura8\System\Security\DataClean;
|
||
|
|
use Hura8\System\Security\DataType;
|
||
|
|
|
||
|
|
|
||
|
|
class RelationModel
|
||
|
|
{
|
||
|
|
|
||
|
|
/* @var $db iConnectDB */
|
||
|
|
protected $db;
|
||
|
|
|
||
|
|
protected $tb_relation = "tb_relation";
|
||
|
|
|
||
|
|
protected $main_item_type= '';
|
||
|
|
protected $main_item_id= 0;
|
||
|
|
|
||
|
|
public function __construct($main_item_type, $main_item_id = 0) {
|
||
|
|
$this->db = get_db('', ENABLE_DB_DEBUG);
|
||
|
|
$this->main_item_type = $main_item_type;
|
||
|
|
$this->main_item_id = $main_item_id;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//given related_type and type_id, get all the main item ids
|
||
|
|
public function getMainItems($related_item_type, $related_item_id = 0, array $condition = [], $return_type = 'list') {
|
||
|
|
|
||
|
|
$where_clause = " AND `main_item_type` = '" . $this->db->escape($this->main_item_type) . "'
|
||
|
|
AND `main_item_id` = '".intval($this->main_item_id)."' ";
|
||
|
|
|
||
|
|
$where_clause .= " AND `related_item_type` = '" . $this->db->escape($related_item_type) . "' ";
|
||
|
|
|
||
|
|
if($related_item_id) $where_clause .= " AND `related_item_id` = '".intval($related_item_id)."' ";
|
||
|
|
|
||
|
|
//excluded_ids
|
||
|
|
if(isset($condition["excluded_ids"]) && $condition["excluded_ids"] ){
|
||
|
|
$list_ids = DataClean::makeListOfInputSafe(explode(",", $condition["excluded_ids"]), DataType::INTEGER);
|
||
|
|
|
||
|
|
if(sizeof($list_ids)) $where_clause .= " AND `related_item_id` NOT IN (".join(',', $list_ids ).") ";
|
||
|
|
}
|
||
|
|
|
||
|
|
//included_ids
|
||
|
|
if(isset($condition["included_ids"]) && $condition["included_ids"] ){
|
||
|
|
$list_ids = DataClean::makeListOfInputSafe(explode(",", $condition["included_ids"]), DataType::INTEGER);
|
||
|
|
if(sizeof($list_ids)) $where_clause .= " AND `related_item_id` IN (".join(',', $list_ids ).") ";
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
if($return_type == 'total') {
|
||
|
|
|
||
|
|
$query = $this->db->query("
|
||
|
|
SELECT COUNT(*) as total FROM `".$this->tb_relation."`
|
||
|
|
WHERE 1 " . $where_clause . " ");
|
||
|
|
|
||
|
|
$total = 0;
|
||
|
|
if ($rs = $this->db->fetchAssoc($query)) {
|
||
|
|
$total = $rs['total'];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $total;
|
||
|
|
|
||
|
|
} else {
|
||
|
|
|
||
|
|
$page = isset($condition['page']) ? intval($condition['page']) : 1;
|
||
|
|
$numPerPage = isset($condition['numPerPage']) ? intval($condition['numPerPage']) : 10;
|
||
|
|
|
||
|
|
$query = $this->db->query("
|
||
|
|
SELECT `related_item_id` FROM `". $this->tb_relation ."`
|
||
|
|
WHERE 1 " . $where_clause . "
|
||
|
|
ORDER BY `ordering` DESC, `id` DESC
|
||
|
|
LIMIT " . ($page - 1) * $numPerPage . ", ". $numPerPage ."
|
||
|
|
");
|
||
|
|
|
||
|
|
$result = array();
|
||
|
|
foreach ( $this->db->fetchAll($query) as $info ) {
|
||
|
|
$result[] = $info['related_item_id'];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public function updateOrdering($related_item_id, $new_order) {
|
||
|
|
|
||
|
|
$this->db->runQuery(
|
||
|
|
"UPDATE `".$this->tb_relation."` SET
|
||
|
|
`ordering` = ?
|
||
|
|
WHERE `main_item_type` = ? AND `main_item_id` = ? AND `related_item_id` = ? ",
|
||
|
|
['d', 's', 'd', 'd'],
|
||
|
|
[ $new_order, $this->main_item_type, $this->main_item_id, $related_item_id ]
|
||
|
|
);
|
||
|
|
|
||
|
|
return [
|
||
|
|
'status' => 'success',
|
||
|
|
'message' => ''
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
//@warn: this does not check if records exist.
|
||
|
|
public function create(array $related_items, $both_way_relation = true) {
|
||
|
|
|
||
|
|
$build_insert = array();
|
||
|
|
foreach ($related_items as $item) {
|
||
|
|
if(!$this->checkExist($this->main_item_type, $this->main_item_id, $item['type'], $item['id'])) {
|
||
|
|
$build_insert[] = [
|
||
|
|
"main_item_type" => $this->main_item_type,
|
||
|
|
"main_item_id" => intval($this->main_item_id),
|
||
|
|
"related_item_type" => $item['type'],
|
||
|
|
"related_item_id" => intval($item['id']),
|
||
|
|
"create_time" => CURRENT_TIME,
|
||
|
|
"create_by" => ADMIN_NAME,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
//if 2-way relation, create item->main
|
||
|
|
if($both_way_relation) {
|
||
|
|
if(!$this->checkExist($item['type'], $item['id'], $this->main_item_type, $this->main_item_id)) {
|
||
|
|
|
||
|
|
$build_insert[] = [
|
||
|
|
"main_item_type" => $item['type'],
|
||
|
|
"main_item_id" => intval($item['id']),
|
||
|
|
"related_item_type" => $this->main_item_type,
|
||
|
|
"related_item_id" => intval($this->main_item_id),
|
||
|
|
"create_time" => CURRENT_TIME,
|
||
|
|
"create_by" => ADMIN_NAME,
|
||
|
|
];
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(sizeof($build_insert)) {
|
||
|
|
$this->db->bulk_insert($this->tb_relation, $build_insert);
|
||
|
|
|
||
|
|
return [
|
||
|
|
'status' => 'success',
|
||
|
|
'message' => ''
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
'status' => 'error',
|
||
|
|
'message' => 'Đã tồn tại'
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
public function checkExist($main_item_type, $main_item_id, $related_item_type, $related_item_id) {
|
||
|
|
|
||
|
|
// itself
|
||
|
|
if($main_item_type == $related_item_type && $main_item_id == $related_item_id) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
$query = $this->db->runQuery(
|
||
|
|
"SELECT id FROM `".$this->tb_relation."`
|
||
|
|
WHERE `main_item_type` = ? AND `main_item_id` = ? AND `related_item_type` = ? AND `related_item_id` = ?
|
||
|
|
LIMIT 1" ,
|
||
|
|
[
|
||
|
|
's', 'd', 's', 'd'
|
||
|
|
],
|
||
|
|
[
|
||
|
|
$main_item_type,
|
||
|
|
intval($main_item_id),
|
||
|
|
$related_item_type,
|
||
|
|
intval($related_item_id)
|
||
|
|
]
|
||
|
|
);
|
||
|
|
|
||
|
|
if($rs = $this->db->fetchAssoc($query)) {
|
||
|
|
return $rs['id'];
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//remove a related-item
|
||
|
|
public function remove($related_item_type, $related_item_id, $remove_both_way = true) {
|
||
|
|
|
||
|
|
$main_item_query = ( $this->main_item_id ) ? " `main_item_id` = '".intval($this->main_item_id)."' AND " : "";
|
||
|
|
|
||
|
|
$this->db->query("DELETE FROM `".$this->tb_relation."` WHERE
|
||
|
|
`main_item_type` = '" . $this->db->escape($this->main_item_type) . "' AND
|
||
|
|
". $main_item_query ."
|
||
|
|
`related_item_type` = '".$this->db->escape($related_item_type)."' AND
|
||
|
|
`related_item_id` = '".intval($related_item_id)."' ");
|
||
|
|
|
||
|
|
if($remove_both_way) {
|
||
|
|
|
||
|
|
$related_item_query = ( $this->main_item_id ) ? " `related_item_id` = '".intval($this->main_item_id)."' AND " : "";
|
||
|
|
|
||
|
|
$this->db->query("DELETE FROM `".$this->tb_relation."` WHERE
|
||
|
|
`related_item_type` = '" . $this->db->escape($this->main_item_type) . "' AND
|
||
|
|
" . $related_item_query . "
|
||
|
|
`main_item_type` = '".$this->db->escape($related_item_type)."' AND
|
||
|
|
`main_item_id` = '".intval($related_item_id)."' ");
|
||
|
|
}
|
||
|
|
|
||
|
|
return [
|
||
|
|
'status' => 'success',
|
||
|
|
'message' => ''
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//remove all relate items
|
||
|
|
public function truncate() {
|
||
|
|
$this->db->runQuery(
|
||
|
|
"DELETE FROM `".$this->tb_relation."` WHERE `main_item_type` = ? AND `main_item_id` = ? ",
|
||
|
|
[ 's', 's' ],
|
||
|
|
[ $this->main_item_type, $this->main_item_id ]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//count related items
|
||
|
|
public function getRelatedItemCount() {
|
||
|
|
|
||
|
|
$query = $this->db->runQuery(
|
||
|
|
"
|
||
|
|
SELECT `related_item_type`, COUNT(`related_item_id`) AS total FROM `". $this->tb_relation ."`
|
||
|
|
WHERE `main_item_type` = ? AND `main_item_id` = ?
|
||
|
|
GROUP BY `related_item_type`
|
||
|
|
",
|
||
|
|
[ 's', 's' ], [ $this->main_item_type, $this->main_item_id ]
|
||
|
|
);
|
||
|
|
$result = array();
|
||
|
|
foreach ( $this->db->fetchAll($query) as $info ) {
|
||
|
|
$result[$info['related_item_type']] = $info['total'];
|
||
|
|
}
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//extend getRelatedItems to get for list of main_item_ids
|
||
|
|
public function getRelatedItemsForList(array $main_item_list_ids, array $related_item_types = []) {
|
||
|
|
|
||
|
|
if(!sizeof($main_item_list_ids)) return [];
|
||
|
|
|
||
|
|
$bind_values = $main_item_list_ids;
|
||
|
|
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($main_item_list_ids, 'int');
|
||
|
|
$condition = " AND `main_item_id` IN (". $parameterized_ids .") ";
|
||
|
|
|
||
|
|
$bind_types[] = 's';
|
||
|
|
$bind_values[] = $this->main_item_type;
|
||
|
|
$condition .= " AND `main_item_type` = ? ";
|
||
|
|
|
||
|
|
if(sizeof($related_item_types)) {
|
||
|
|
$type_condition = [];
|
||
|
|
foreach ($related_item_types as $_type) {
|
||
|
|
$type_condition[] = " `related_item_type` = ? ";
|
||
|
|
|
||
|
|
$bind_types[] = 's';
|
||
|
|
$bind_values[] = $_type;
|
||
|
|
}
|
||
|
|
|
||
|
|
$condition .= " AND ( ".join(' OR ', $type_condition )." ) ";
|
||
|
|
}
|
||
|
|
|
||
|
|
$query = $this->db->runQuery("
|
||
|
|
SELECT
|
||
|
|
`main_item_id` ,
|
||
|
|
`related_item_type`,
|
||
|
|
`related_item_id`,
|
||
|
|
`ordering`
|
||
|
|
FROM `". $this->tb_relation ."`
|
||
|
|
WHERE 1 ". $condition ."
|
||
|
|
ORDER BY `ordering` DESC, `id` DESC
|
||
|
|
LIMIT 5000
|
||
|
|
", $bind_types, $bind_values);
|
||
|
|
|
||
|
|
$result = array();
|
||
|
|
foreach ( $this->db->fetchAll($query) as $info ) {
|
||
|
|
$result[$info['main_item_id']][$info['related_item_type']][$info['related_item_id']] = [
|
||
|
|
"item_id" => $info['related_item_id'],
|
||
|
|
"ordering" => $info['ordering'],
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
//final result
|
||
|
|
$final_result = [];
|
||
|
|
foreach ($main_item_list_ids as $_id) {
|
||
|
|
$final_result[$_id] = (isset($result[$_id])) ? $result[$_id] : false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $final_result;
|
||
|
|
}
|
||
|
|
|
||
|
|
//get all related items and group them by type
|
||
|
|
public function getRelatedItems(array $related_item_types = []) {
|
||
|
|
|
||
|
|
$bind_types = ['s', 's'];
|
||
|
|
$bind_values = [$this->main_item_type, $this->main_item_id];
|
||
|
|
|
||
|
|
$condition = "";
|
||
|
|
if(sizeof($related_item_types)) {
|
||
|
|
$type_condition = [];
|
||
|
|
foreach ($related_item_types as $_type) {
|
||
|
|
$type_condition[] = " `related_item_type` = ? ";
|
||
|
|
|
||
|
|
$bind_types[] = 's';
|
||
|
|
$bind_values[] = $_type;
|
||
|
|
}
|
||
|
|
|
||
|
|
$condition .= " AND ( ".join(' OR ', $type_condition )." ) ";
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
$query = $this->db->runQuery("
|
||
|
|
SELECT
|
||
|
|
`related_item_type`,
|
||
|
|
`related_item_id`,
|
||
|
|
`ordering`
|
||
|
|
FROM `". $this->tb_relation ."`
|
||
|
|
WHERE `main_item_type` = ? AND `main_item_id` = ? ". $condition ."
|
||
|
|
ORDER BY `ordering` DESC, `id` DESC
|
||
|
|
LIMIT 5000
|
||
|
|
", $bind_types, $bind_values );
|
||
|
|
$result = array();
|
||
|
|
foreach ( $this->db->fetchAll($query) as $info ) {
|
||
|
|
$result[$info['related_item_type']][$info['related_item_id']] = [
|
||
|
|
"item_id" => $info['related_item_id'],
|
||
|
|
"ordering" => $info['ordering'],
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
//check if we get only single type, then return all the results
|
||
|
|
if(sizeof($related_item_types) == 1) {
|
||
|
|
$related_item_type = $related_item_types[0];
|
||
|
|
return isset($result[$related_item_type]) ? $result[$related_item_type] : array();
|
||
|
|
}
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|