This commit is contained in:
2024-01-29 10:46:02 +07:00
181 changed files with 20765 additions and 408 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
package/vendor
package/composer.lock
.idea

7
.htaccess Normal file
View File

@@ -0,0 +1,7 @@
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/index.php
# prevent rewrite non-existent files
RewriteCond %{REQUEST_URI} !\.(jpg|png|gif|css|js|php|tiff|jpeg|ico)$
RewriteRule ^(.*)$ /index.php [QSA,L]

View File

@@ -1,8 +1,17 @@
<?php
define("ROOT_DIR", __DIR__ );
const ROOT_DIR = __DIR__;
const CONFIG_DIR = ROOT_DIR . '/inc/config';
include __DIR__."/inc/common.php";
const IMAGE_FILE_SEPARATOR = "-";
define("CURRENT_TIME", time());
const STATIC_DOMAIN = "http://hura8.hurasoft.com";
const ENABLE_DB_DEBUG = true;
const LANGUAGE = 'vi';
const IS_DEFAULT_LANGUAGE = true;
include ROOT_DIR."/inc/common.php";
include ROOT_DIR."/inc/fun.db.php";
// start autoload
init_autoload();

View File

@@ -141,7 +141,7 @@ a {
overflow: auto;
background: #004e99;
color: #fff;
width: 20%;
width: 18%;
padding: 20px 0;
line-height: 20px;
}
@@ -494,6 +494,12 @@ a {
line-height: 20px;
}
.admin-content-container {
width: calc(100% - 50px);
}
.admin-content-container.show-large-menu {
width: 82%;
}
.admin-content-container .note-list {
min-width: 144px;
}
@@ -719,6 +725,9 @@ input[type=radio]:focus:before {
.order-page .order-page-table td {
padding: 14px 8px;
}
.order-page .order-page-table td:nth-child(4), .order-page .order-page-table td:nth-child(10) {
text-align: left;
}
.order-page .order-page-table .icons {
width: 30px;
height: 30px;
@@ -751,6 +760,7 @@ input[type=radio]:focus:before {
.order-page-table thead {
background: #F6F6F6;
font-weight: 600;
text-align: center;
}
.order-page-table thead td {
padding: 8px;

File diff suppressed because one or more lines are too long

View File

@@ -121,7 +121,7 @@ a {
overflow: auto;
background: #004e99;
color: #fff;
width: 20%;
width: 18%;
padding: 20px 0;
line-height: 20px;
a {
@@ -483,6 +483,10 @@ a {
}
}
.admin-content-container {
width: calc(100% - 50px);
&.show-large-menu {
width: 82%;
}
.note-list {
min-width: 144px;
a {
@@ -734,6 +738,9 @@ input[type="radio"] {
}
td{
padding: 14px 8px;
&:nth-child(4), &:nth-child(10){
text-align: left;
}
}
.icons {
width: 30px;
@@ -767,6 +774,7 @@ input[type="radio"] {
thead {
background: #F6F6F6;
font-weight: 600;
text-align: center;
td{
padding: 8px;
}

View File

@@ -9,7 +9,7 @@ function _get_menu_list() {
'product',
'customer',
'marketing',
'investor_relation',
//'investor_relation',
'article',
'job',
'pcbuilder',
@@ -21,8 +21,21 @@ function _get_menu_list() {
$menu_list = [];
foreach ($main_items as $item) {
$menu_list[$item] = include_once __DIR__."/menu_item/".$item.".php";
$item_menu = include_once __DIR__."/menu_item/".$item.".php";
// skip not enabled
if(!$item_menu['enable']) continue;
// skip not enabled children
$item_menu_children_enabled = array_filter($item_menu['menu'], function ($menu_child) { return $menu_child['enable'];});
if(!sizeof($item_menu_children_enabled)) continue;
// reset menu for $item_menu
$item_menu['menu'] = $item_menu_children_enabled;
$menu_list[$item] = $item_menu;
}
return $menu_list;
}

View File

@@ -12,7 +12,7 @@ return array(
'view' => 'home',
'id' => 'article/home',
'name' => 'Tin bài',
'url' => '/admin/?opt=article',
'url' => '/admin/article',
),
array(
@@ -21,7 +21,7 @@ return array(
'view' => 'home',
'id' => 'media/home',
'name' => 'Thư viện file Media',
'url' => '/admin/?opt=media',
'url' => '/admin/media',
),
array(
@@ -30,7 +30,7 @@ return array(
'view' => 'home',
'id' => 'page/home',
'name' => 'Nội dung cố định',
'url' => '/admin/?opt=page',
'url' => '/admin/page',
),
array(
@@ -39,7 +39,7 @@ return array(
'view' => 'home',
'id' => 'album/home',
'name' => 'Thư viện ảnh',
'url' => '/admin/?opt=album',
'url' => '/admin/album',
),
array(
@@ -48,7 +48,7 @@ return array(
'view' => 'home',
'id' => 'video/home',
'name' => 'Video',
'url' => '/admin/?opt=video',
'url' => '/admin/video',
),
),
);

View File

@@ -12,7 +12,7 @@ return array(
'view' => 'home',
'id' => 'ask/home',
'name' => 'Danh sách câu hỏi',
'url' => '/admin/?opt=ask',
'url' => '/admin/ask',
),
1 => array(
@@ -21,7 +21,7 @@ return array(
'view' => 'answer-list',
'id' => 'ask/answer-list',
'name' => 'Danh sách trả lời',
'url' => '/admin/?opt=ask&view=answer-list',
'url' => '/admin/ask/answer-list',
),
2 => array(
@@ -30,7 +30,7 @@ return array(
'view' => 'category',
'id' => 'ask/ask',
'name' => 'Danh mục hỏi đáp',
'url' => '/admin/?opt=ask&view=category',
'url' => '/admin/ask/category',
),
),
);

View File

@@ -12,7 +12,7 @@ return array(
'view' => 'home',
'id' => 'customer/home',
'name' => 'Danh sách khách hàng',
'url' => '/admin/?opt=customer',
'url' => '/admin/customer',
),
/*array(
@@ -21,7 +21,7 @@ return array(
'view' => 'customer-group',
'id' => 'customer/customer-group',
'name' => 'Nhóm khách hàng',
'url' => '/admin/?opt=customer&view=customer-group',
'url' => '/admin/customer/customer-group',
),*/
array(
@@ -30,7 +30,7 @@ return array(
'view' => 'customer-contact',
'id' => 'customer/customer-contact',
'name' => 'Khách hàng liên hệ',
'url' => '/admin/?opt=customer&view=customer-contact',
'url' => '/admin/customer/customer-contact',
),
array(
@@ -39,7 +39,7 @@ return array(
'view' => 'comment',
'id' => 'customer/comment',
'name' => 'Tổng hợp trao đổi',
'url' => '/admin/?opt=customer&view=comment',
'url' => '/admin/customer/comment',
),
array(
@@ -48,7 +48,7 @@ return array(
'view' => 'review',
'id' => 'customer/review',
'name' => 'Tổng hợp Đánh giá',
'url' => '/admin/?opt=customer&view=review',
'url' => '/admin/customer/review',
),
array(
@@ -57,7 +57,7 @@ return array(
'view' => 'customer-review',
'id' => 'customer/customer-review',
'name' => 'Khách hàng góp ý',
'url' => '/admin/?opt=customer&view=customer-review',
'url' => '/admin/customer/customer-review',
),
array(
@@ -66,10 +66,8 @@ return array(
'view' => 'customer-newsletter',
'id' => 'customer/customer-newsletter',
'name' => 'Khách hàng nhận bản tin',
'url' => '/admin/?opt=customer&view=customer-newsletter',
'url' => '/admin/customer/customer-newsletter',
),
),
);

View File

@@ -11,7 +11,7 @@ return array(
'view' => 'home',
'id' => 'distributor/home',
'name' => 'Danh sách',
'url' => '/admin/?opt=distributor',
'url' => '/admin/distributor',
),
),
);

View File

@@ -11,7 +11,7 @@ return array(
'view' => 'annual_report',
'name' => 'Báo cáo thường niên',
'id' => 'investor_relation/annual_report',
'url' => '/admin/?opt=investor_relation&view=annual_report',
'url' => '/admin/investor_relation/annual_report',
),
array(
'enable' => true ,
@@ -19,7 +19,7 @@ return array(
'view' => 'charter',
'name' => 'Điều lệ hoạt động',
'id' => 'investor_relation/charter',
'url' => '/admin/?opt=investor_relation&view=charter',
'url' => '/admin/investor_relation/charter',
),
array(
'enable' => true ,
@@ -27,7 +27,7 @@ return array(
'view' => 'governance',
'name' => 'Quy chế quản trị',
'id' => 'investor_relation/governance',
'url' => '/admin/?opt=investor_relation&view=governance',
'url' => '/admin/investor_relation/governance',
),
array(
'enable' => true ,
@@ -35,7 +35,7 @@ return array(
'view' => 'financial_reports',
'name' => 'Báo cáo tài chính',
'id' => 'investor_relation/financial_reports',
'url' => '/admin/?opt=investor_relation&view=financial_reports',
'url' => '/admin/investor_relation/financial_reports',
),
array(
'enable' => true ,
@@ -43,7 +43,7 @@ return array(
'view' => 'info_disclosure',
'name' => 'Công bố thông tin',
'id' => 'investor_relation/info_disclosure',
'url' => '/admin/?opt=investor_relation&view=info_disclosure',
'url' => '/admin/investor_relation/info_disclosure',
),
array(
'enable' => true ,
@@ -51,7 +51,7 @@ return array(
'view' => 'management_report',
'name' => 'Báo cáo quản trị',
'id' => 'investor_relation/management_report',
'url' => '/admin/?opt=investor_relation&view=management_report',
'url' => '/admin/investor_relation/management_report',
),
array(
'enable' => true ,
@@ -59,7 +59,7 @@ return array(
'view' => 'shareholder_meeting',
'name' => 'Đại hội cổ đông',
'id' => 'investor_relation/shareholder_meeting',
'url' => '/admin/?opt=investor_relation&view=shareholder_meeting',
'url' => '/admin/investor_relation/shareholder_meeting',
),
array(
'enable' => true ,
@@ -67,7 +67,7 @@ return array(
'view' => 'whitepaper',
'name' => 'Báo cáo bạch',
'id' => 'investor_relation/whitepaper',
'url' => '/admin/?opt=investor_relation&view=whitepaper',
'url' => '/admin/investor_relation/whitepaper',
),
),
);

View File

@@ -5,14 +5,13 @@ return array(
"name" => "Tuyển dụng",
"url" => "",
"menu" => array(
array(
'enable' => true ,
'module' => 'job',
'view' => 'home',
'id' => 'job/home',
'name' => 'Vị trí tuyển',
'url' => '/admin/?opt=job',
'url' => '/admin/job',
),
array(
@@ -21,7 +20,7 @@ return array(
'view' => 'home',
'id' => 'job/home',
'name' => 'Quản lý hồ sơ',
'url' => '/admin/?opt=job&view=applicants',
'url' => '/admin/job/applicants',
),
),
);

View File

@@ -2,7 +2,6 @@
return array(
'enable' => true ,
"name" => "Marketing",
"url" => "",
"menu" => array(
@@ -12,7 +11,7 @@ return array(
'view' => 'promotion',
'id' => 'marketing/promotion',
'name' => 'Khuyến mại theo sản phẩm',
'url' => '/admin/?opt=marketing&view=promotion',
'url' => '/admin/marketing/promotion',
),
array(
@@ -21,7 +20,7 @@ return array(
'view' => 'coupon',
'id' => 'marketing/coupon',
'name' => 'Phiếu giảm giá - Voucher',
'url' => '/admin/?opt=marketing&view=coupon',
'url' => '/admin/marketing/coupon',
),
array(
@@ -30,7 +29,7 @@ return array(
'view' => 'home',
'id' => 'banner/home',
'name' => 'Danh sách banner',
'url' => '/admin/?opt=banner',
'url' => '/admin/banner',
),
array(
@@ -39,7 +38,7 @@ return array(
'view' => 'store-design',
'id' => 'system/store-design',
'name' => 'Banner pop-up',
'url' => '/admin/?opt=system&view=store-design&section=popup',
'url' => '/admin/system/store-design&section=popup',
),
array(
@@ -48,26 +47,26 @@ return array(
'view' => 'poster-upload',
'id' => 'marketing/poster-upload',
'name' => 'Poster',
'url' => '/admin/?opt=marketing&view=poster-upload',
'url' => '/admin/marketing/poster-upload',
),
array(
/* array(
'enable' => true ,
'module' => 'system',
'view' => 'store-design',
'id' => 'system/store-design',
'name' => 'Hình nền website',
'url' => '/admin/?opt=system&view=store-design&section=background',
),
'url' => '/admin/system/store-design&section=background',
),*/
array(
/* array(
'enable' => false,
'module' => 'email',
'view' => 'home',
'id' => 'email/home',
'name' => 'Email',
'url' => '/admin/?opt=email',
),
'url' => '/admin/email',
),*/
array(
'enable' => false,
@@ -75,7 +74,7 @@ return array(
'view' => 'meta-list',
'id' => 'url/meta-list',
'name' => 'Sửa thông tin URL',
'url' => '/admin/?opt=url&view=meta-list',
'url' => '/admin/url/meta-list',
),
array(
@@ -84,7 +83,7 @@ return array(
'view' => 'url-seo',
'id' => 'url/url-seo',
'name' => 'Link SEO',
'url' => '/admin/?opt=url&view=url-seo',
'url' => '/admin/url/url-seo',
),
array(
@@ -93,7 +92,7 @@ return array(
'view' => 'feed',
'id' => 'marketing/feed',
'name' => 'Facebook/Google Feed',
'url' => '/admin/?opt=marketing&view=feed',
'url' => '/admin/marketing/feed',
),
array(
@@ -102,7 +101,7 @@ return array(
'view' => 'product-offer',
'id' => 'marketing/product-offer',
'name' => 'Cài biểu tượng giảm giá',
'url' => '/admin/?opt=marketing&view=product-offer',
'url' => '/admin/marketing/product-offer',
),
/*array(
@@ -111,7 +110,7 @@ return array(
'view' => 'lead-program',
'id' => 'marketing/lead-program',
'name' => 'Thu thập khách hàng',
'url' => '/admin/?opt=marketing&view=lead-program',
'url' => '/admin/marketing/lead-program',
),*/
array(
@@ -120,7 +119,7 @@ return array(
'view' => 'program',
'id' => 'marketing/program',
'name' => 'Chương trình khuyến mại',
'url' => '/admin/?opt=marketing&view=program',
'url' => '/admin/marketing/program',
),
array(
@@ -129,7 +128,7 @@ return array(
'view' => 'home',
'id' => 'deal/home',
'name' => 'Deal/giờ vàng',
'url' => '/admin/?opt=deal',
'url' => '/admin/deal',
),
array(
@@ -138,7 +137,7 @@ return array(
'view' => 'combo-deal',
'id' => 'marketing/combo-deal',
'name' => 'Bán Combo',
'url' => '/admin/?opt=marketing&view=combo-deal',
'url' => '/admin/marketing/combo-deal',
),
array(
@@ -147,7 +146,7 @@ return array(
'view' => 'conditional-promotion',
'id' => 'marketing/conditional-promotion',
'name' => 'Khuyến mại BuildPC',
'url' => '/admin/?opt=marketing&view=conditional-promotion',
'url' => '/admin/marketing/conditional-promotion',
),
array(
@@ -156,7 +155,7 @@ return array(
'view' => 'live-support-per-category',
'id' => 'marketing/live-support-per-category',
'name' => 'Hỗ trợ theo từng danh mục',
'url' => '/admin/?opt=marketing&view=live-support-per-category',
'url' => '/admin/marketing/live-support-per-category',
),
),

View File

@@ -11,16 +11,16 @@ return array(
'view' => 'home',
'id' => 'order/home',
'name' => 'Danh sách đơn hàng',
'url' => '/admin/?opt=order', // &list=new
'url' => '/admin/order', // &list=new
),
array(
'enable' => true ,
'enable' => false ,
'module' => 'order',
'view' => 'bargain',
'id' => 'order/bargain',
'name' => 'Mặc cả giá',
'url' => '/admin/?opt=order&view=bargain',
'url' => '/admin/order/bargain',
),
array(
@@ -29,7 +29,7 @@ return array(
'view' => 'wait-order',
'id' => 'order/wait-order',
'name' => 'Chờ mua sản phẩm',
'url' => '/admin/?opt=order&view=wait-order',
'url' => '/admin/order/wait-order',
),
@@ -39,7 +39,7 @@ return array(
'view' => 'paygate',
'id' => 'order/paygate',
'name' => 'Thanh toán qua cổng dịch vụ',
'url' => '/admin/?opt=order&view=paygate',
'url' => '/admin/order/paygate',
),
array(
@@ -48,7 +48,7 @@ return array(
'view' => 'price-quote',
'id' => 'order/price-quote',
'name' => 'Lập báo giá',
'url' => '/admin/?opt=order&view=price-quote',
'url' => '/admin/order/price-quote',
),
),
);

View File

@@ -2,35 +2,34 @@
return array(
'enable' => true ,
"name" => "Trả góp",
"url" => "",
"menu" => array(
0 => array(
array(
'enable' => true ,
'module' => 'payinstall',
'view' => 'order',
'id' => 'payinstall/order',
'name' => 'Danh sách đơn hàng',
'url' => '/admin/?opt=payinstall&view=order',
'url' => '/admin/payinstall/order',
),
1 => array(
array(
'enable' => true ,
'module' => 'payinstall',
'view' => 'home',
'id' => 'payinstall/home',
'name' => 'Cài đặt trả góp',
'url' => '/admin/?opt=payinstall',
'url' => '/admin/payinstall',
),
2 => array(
array(
'enable' => true ,
'module' => 'payinstall',
'view' => 'category',
'id' => 'payinstall/category',
'name' => 'Danh mục sản phẩm',
'url' => '/admin/?opt=payinstall&view=category',
'url' => '/admin/payinstall/category',
),
),
);

View File

@@ -12,7 +12,7 @@ return array(
'view' => 'home',
'id' => 'pcbuilder/home',
'name' => 'Linh kiện xây dựng',
'url' => '/admin/?opt=pcbuilder',
'url' => '/admin/pcbuilder',
),
array(
@@ -21,7 +21,7 @@ return array(
'view' => 'preconfig',
'id' => 'pcbuilder/preconfig',
'name' => 'Bộ máy tính mẫu',
'url' => '/admin/?opt=pcbuilder&view=preconfig',
'url' => '/admin/pcbuilder/preconfig',
),
),

View File

@@ -4,7 +4,6 @@
return array(
'enable' => true ,
"name" => "Sản phẩm",
"url" => "",
"menu" => array(
@@ -14,35 +13,35 @@ return array(
'view' => 'home',
'id' => 'product/home',
'name' => 'Sản phẩm',
'url' => '/admin/?opt=product',
'url' => '/admin/product',
),
array(
/* array(
'enable' => false ,
'module' => 'product',
'view' => 'user-rating',
'id' => 'product/user-rating',
'name' => 'Đánh giá ',
'url' => '/admin/?opt=product&view=user-rating',
),
'url' => '/admin/product/user-rating',
),*/
array(
/*array(
'enable' => false ,
'module' => 'product',
'view' => 'product-customer-image',
'id' => 'product/product-customer-image',
'name' => 'Ảnh người dùng gửi',
'url' => '/admin/?opt=product&view=product-customer-image',
),
'url' => '/admin/product/product-customer-image',
),*/
array(
/*array(
'enable' => true ,
'module' => 'product',
'view' => 'product-list-update',
'id' => 'product/product-list-update',
'name' => 'Bảng giá cập nhật',
'url' => '/admin/?opt=product&view=product-list-update',
),
'url' => '/admin/product/product-list-update',
),*/
array(
'enable' => true ,
@@ -50,7 +49,7 @@ return array(
'view' => 'category',
'id' => 'product/category',
'name' => 'Danh mục',
'url' => '/admin/?opt=product&view=category',
'url' => '/admin/product/category',
),
array(
'enable' => true ,
@@ -58,7 +57,7 @@ return array(
'view' => 'home',
'id' => 'brand/home',
'name' => 'Thương hiệu',
'url' => '/admin/?opt=brand',
'url' => '/admin/brand',
),
array(
'enable' => true ,
@@ -66,7 +65,7 @@ return array(
'view' => 'attribute',
'id' => 'product/attribute',
'name' => 'Thuộc tính ',
'url' => '/admin/?opt=product&view=attribute',
'url' => '/admin/product/attribute',
),
array(
'enable' => true ,
@@ -74,7 +73,7 @@ return array(
'view' => 'collection',
'id' => 'product/collection',
'name' => 'Bộ sưu tập',
'url' => '/admin/?opt=product&view=collection',
'url' => '/admin/product/collection',
),
array(
@@ -83,79 +82,61 @@ return array(
'view' => 'product-wait-list',
'id' => 'product/product-wait-list',
'name' => 'Chờ mua sản phẩm',
'url' => '/admin/?opt=product&view=product-wait-list',
'url' => '/admin/product/product-wait-list',
),
array(
'enable' => false ,
'enable' => true ,
'module' => 'addon',
'view' => 'home',
'id' => 'addon/home',
'name' => 'SP/Dịch vụ mua kèm',
'url' => '/admin/?opt=addon',
'url' => '/admin/addon',
),
array(
'enable' => false ,
'enable' => true ,
'module' => 'config_group',
'view' => 'home',
'id' => 'config_group/home',
'name' => 'Nhóm cấu hình',
'url' => '/admin/?opt=config_group',
'url' => '/admin/config_group',
),
array(
/*array(
'enable' => false ,
'module' => 'supplier',
'view' => 'home',
'id' => 'supplier/home',
'name' => 'Nhà cung cấp',
'url' => '/admin/?opt=supplier',
),
'url' => '/admin/supplier',
),*/
array(
'enable' => false ,
'module' => 'product',
'view' => 'product-erp-not-web',
'id' => 'product/product-erp-not-web',
'name' => 'SP ERP chưa có trên web',
'url' => '/admin/?opt=product&view=product-erp-not-web',
),
array(
/* array(
'enable' => false ,
'module' => 'product',
'view' => 'set-promotion-price',
'id' => 'product/set-promotion-price',
'name' => 'Cài đặt giá khuyến mại',
'url' => '/admin/?opt=product&view=set-promotion-price',
),
'url' => '/admin/product/set-promotion-price',
),*/
array(
/*array(
'enable' => false ,
'module' => 'product',
'view' => 'product-per-customer-group',
'id' => 'product/product-per-customer-group',
'name' => 'Giá theo nhóm khách hàng',
'url' => '/admin/?opt=product&view=product-per-customer-group',
),
'url' => '/admin/product/product-per-customer-group',
),*/
array(
'enable' => false ,
'module' => 'product',
'view' => 'list-competitor',
'id' => 'product/list-competitor',
'name' => 'So sánh giá đối thủ',
'url' => '/admin/?opt=product&view=list-competitor',
),
array(
'enable' => false ,
'enable' => true ,
'module' => 'product',
'view' => 'set',
'id' => 'product/set',
'name' => 'Set sản phẩm',
'url' => '/admin/?opt=product&view=set',
'url' => '/admin/product/set',
),
array(
@@ -164,8 +145,7 @@ return array(
'view' => 'spec-group',
'id' => 'product/spec-group',
'name' => 'Nhóm thông số kỹ thuật',
'url' => '/admin/?opt=product&view=spec-group',
'url' => '/admin/product/spec-group',
),
),
);

View File

@@ -6,76 +6,76 @@ return array(
"name" => "Thống kê",
"url" => "",
"menu" => array(
0 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'visitor',
'id' => 'report/visitor',
'name' => 'Thống kê truy cập',
'url' => '/admin/?opt=report&view=visitor',
'url' => '/admin/report/visitor',
),
1 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'customer',
'id' => 'report/customer',
'name' => 'Thống kê khách hàng',
'url' => '/admin/?opt=report&view=customer',
'url' => '/admin/report/customer',
),
2 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'order',
'id' => 'report/order',
'name' => 'Thống kê đơn hàng',
'url' => '/admin/?opt=report&view=order',
'url' => '/admin/report/order',
),
3 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'product-buy',
'id' => 'report/product-buy',
'name' => 'Sản phẩm mua nhiều',
'url' => '/admin/?opt=report&view=product-buy',
'url' => '/admin/report/product-buy',
),
4 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'product-visit',
'id' => 'report/product-visit',
'name' => 'Sản phẩm xem nhiều',
'url' => '/admin/?opt=report&view=product-visit',
'url' => '/admin/report/product-visit',
),
5 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'referer',
'id' => 'report/referer',
'name' => 'Web giới thiệu',
'url' => '/admin/?opt=report&view=referer',
'url' => '/admin/report/referer',
),
6 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'search',
'id' => 'report/search',
'name' => 'Từ khóa tìm kiếm',
'url' => '/admin/?opt=report&view=search',
'url' => '/admin/report/search',
),
/*7 => array(
array(
'enable' => true ,
'module' => 'report',
'view' => 'error-page',
'id' => '',
'name' => 'Lỗi website',
'url' => '/admin/?opt=report&view=error-page',
),*/
'url' => '/admin/report/error-page',
),
),
);

View File

@@ -11,7 +11,7 @@ return array(
'view' => 'settings',
'id' => 'system/settings',
'name' => 'Cài đặt chung',
'url' => '/admin/?opt=system&view=settings',
'url' => '/admin/system/settings',
),
array(
@@ -20,7 +20,7 @@ return array(
'view' => 'home',
'id' => 'template/home',
'name' => 'Sửa file template',
'url' => '/admin/?opt=template',
'url' => '/admin/template',
),
array(
@@ -29,7 +29,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Quản trị viên',
'url' => '/admin/?opt=admin',
'url' => '/admin/admin',
),
array(
@@ -38,7 +38,7 @@ return array(
'view' => 'store-address',
'id' => '',
'name' => 'Địa chỉ cửa hàng',
'url' => '/admin/?opt=system&view=store-address',
'url' => '/admin/system/store-address',
),
/*4 => array(
@@ -47,7 +47,7 @@ return array(
'view' => 'domain',
'id' => 'system/domain',
'name' => 'Cài đặt tên miền',
'url' => '/admin/?opt=system&view=domain',
'url' => '/admin/system/domain',
),*/
/*array(
@@ -56,7 +56,7 @@ return array(
'view' => 'ban-ip',
'id' => '',
'name' => 'Chặn IP truy cập website',
'url' => '/admin/?opt=system&view=ban-ip',
'url' => '/admin/system/ban-ip',
),
array(
@@ -65,7 +65,7 @@ return array(
'view' => 'change-info',
'id' => '',
'name' => 'Thông tin website',
'url' => '/admin/?opt=system&view=change-info',
'url' => '/admin/system/change-info',
),*/
/*
@@ -76,7 +76,7 @@ return array(
'view' => 'province-list',
'id' => '',
'name' => 'Cài đặt tỉnh thành',
'url' => '/admin/?opt=system&view=province-list',
'url' => '/admin/system/province-list',
),*/
/*9 => array(
@@ -85,7 +85,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Tình trạng đơn hàng',
'url' => '/admin/?opt=system&view=order-status',
'url' => '/admin/system/order-status',
),*/
/*array(
@@ -94,7 +94,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Hỗ trợ bán hàng',
'url' => '/admin/?opt=online_support',
'url' => '/admin/online_support',
),*/
/*array(
@@ -103,7 +103,7 @@ return array(
'view' => 'country',
'id' => '',
'name' => 'Quốc gia',
'url' => '/admin/?opt=system&view=country',
'url' => '/admin/system/country',
),*/
/*12 => array(
@@ -112,7 +112,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Cài đặt thông báo ngoài',
'url' => '/admin/?opt=report_out',
'url' => '/admin/report_out',
),*/
/*array(
@@ -121,7 +121,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Cài đặt phí vận chuyển',
'url' => '/admin/?opt=shipping2',
'url' => '/admin/shipping2',
),*/
array(
@@ -130,7 +130,7 @@ return array(
'view' => 'redirect',
'id' => 'url/redirect',
'name' => 'Url Redirect',
'url' => '/admin/?opt=url&view=redirect',
'url' => '/admin/url/redirect',
),
array(
@@ -139,7 +139,7 @@ return array(
'view' => 'home',
'id' => 'tool/home',
'name' => 'Công cụ',
'url' => '/admin/?opt=tool',
'url' => '/admin/tool',
),
/*array(
@@ -148,7 +148,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Civi Affiliate',
'url' => '/admin/?opt=civi',
'url' => '/admin/civi',
),
array(
@@ -157,7 +157,7 @@ return array(
'view' => 'home',
'id' => 'menu/home',
'name' => 'Quản trị menu',
'url' => '/admin/?opt=menu',
'url' => '/admin/menu',
),
array(
@@ -166,7 +166,7 @@ return array(
'view' => 'keyword-suggest',
'id' => 'system/keyword-suggest',
'name' => 'Từ khóa gợi ý',
'url' => '/admin/?opt=system&view=keyword-suggest',
'url' => '/admin/system/keyword-suggest',
),*/
array(
@@ -175,7 +175,7 @@ return array(
'view' => 'home',
'id' => 'tag/home',
'name' => 'Quản trị Tag',
'url' => '/admin/?opt=tag',
'url' => '/admin/tag',
),
),
);

View File

@@ -12,7 +12,7 @@ return array(
'view' => 'home',
'id' => '',
'name' => 'Khách hàng đăng ký',
'url' => '/admin/?opt=warranty&view=customer-register',
'url' => '/admin/warranty/customer-register',
),*/
array(
@@ -21,7 +21,7 @@ return array(
'view' => 'home',
'id' => 'warranty/home',
'name' => 'Danh sách bảo hành',
'url' => '/admin/?opt=warranty',
'url' => '/admin/warranty',
),
),
);

View File

@@ -1 +1,36 @@
<?php
use Hura8\Components\Product\AdminController\AProductController;
use Hura8\System\Paging;
$objAProductController = new AProductController();
//Paging setting
$numPerPage = getPageSize(15);
$conditions = [
"category" => explode("-", getRequest("category", '')),
"brand" => explode("-", getRequest("brand", '')),
"hotType" => explode("-", getRequest("hotType", '')),
"other_filter" => [getRequest("other_filter", '')],
"q" => getRequest("q", ''),
'numPerPage' => $numPerPage,
'page' => getPageId(),
'translated' => getRequestInt('translated', 0),
//... more extended filters
];
//debug_var($objAProductController->getFilterConditions());
$totalResults = $objAProductController->getTotal($conditions);
$item_list = $objAProductController->getList($conditions);
list($page_collection, $tb_page, $total_pages) = Paging::paging_template($totalResults, $numPerPage);
return [
"total" => $totalResults,
"item_list" => $item_list,
"pagination" => [
'collection' => $page_collection,
'html' => $tb_page,
'total_pages' => $total_pages,
],
];

View File

@@ -32,12 +32,13 @@ class AppAdmin
protected function getRouter() {
$route = [
/*$route = [
"module" => (isset($_REQUEST['module'])) ? $_REQUEST['module'] : 'home',
"view" => (isset($_REQUEST['view'])) ? $_REQUEST['view'] : 'home',
];
];*/
$this->current_route_info = $route;
$objRouter = new Router();
$this->current_route_info = $objRouter->getRouting();
}
@@ -48,15 +49,16 @@ class AppAdmin
str_replace("-", "_", $this->current_route_info["view"]).".php"
]) ;
if(!file_exists($module_file)) {
if(file_exists($module_file)) {
// print_r($this->current_route_info);
die('Page '. $module_file .' not found!');
// die('Page '. $module_file .' not found!');
$data = include_once $module_file;
}else{
$data = ['file data '. $module_file .' not found!'];
}
$data = include_once $module_file;
$global_data = [
"name" => $this->current_route_info['module'],
"module" => $this->current_route_info['module'],
"view" => $this->current_route_info['view'],
"main_menu" => include_once ROOT_DIR."/data/menu.php",
];
@@ -71,11 +73,16 @@ class AppAdmin
protected function renderModule() {
$template_file_path = $this->tpl_path ."/". $this->current_route_info['module']."/".$this->current_route_info['view'].".html";
$template_file_path = $this->tpl_path ."/". $this->current_route_info['module'];
$template_file_name = $this->current_route_info['view'].".html";
$template_file_full_path = $template_file_path."/".$template_file_name;
//check exist
if(!@file_exists( $template_file_path)) {
die("Not found : ". $template_file_path);
if(!@file_exists( $template_file_full_path)) {
// attempt to auto create first
if(!$this->autoCreateTplFile( $template_file_path, $template_file_name )) {
die("Please manually create template file at: ". $template_file_full_path);
}
}
$theme_file_path = $this->tpl_path ."/theme.html";
@@ -84,7 +91,7 @@ class AppAdmin
}
$theme_content = @file_get_contents( $theme_file_path );
$module_content = @file_get_contents( $template_file_path );
$module_content = @file_get_contents( $template_file_full_path );
$page_content_to_parse = preg_replace([
"/{{(\s+)?page_content(\s+)?}}/"
@@ -99,6 +106,27 @@ class AppAdmin
);
}
protected function autoCreateTplFile($file_path, $file_name) : bool {
// create dir if not exist
if(!file_exists($file_path)) {
if(!mkdir($file_path, 0755, true)) {
return false;
}
if(!file_exists($file_path)) {
return false;
}
}
//create file
$file_full_path = $file_path . "/". $file_name;
@file_put_contents($file_full_path, $file_full_path);
return file_exists($file_full_path);
}
/*
* 2 ways to render a html template
* 1. Use $html_to_parse, which requires no dependencies

View File

@@ -0,0 +1,165 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\System\Controller\aAdminEntityBaseController;
use Hura8\Components\Product\Model\ProductAttributeLanguageModel;
use Hura8\Components\Product\Model\ProductAttributeModel;
use Hura8\Components\Product\Model\ProductAttributeValueModel;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
class AProductAttributeController extends aAdminEntityBaseController
{
/* @var ProductAttributeModel $objProductAttributeModel */
protected $objProductAttributeModel;
/* @var ProductAttributeLanguageModel $objProductAttributeLanguageModel */
protected $objProductAttributeLanguageModel;
protected $view_language = LANGUAGE;
public function __construct()
{
$this->objProductAttributeModel = new ProductAttributeModel();
if(!$this->isDefaultLanguage()) {
$this->objProductAttributeLanguageModel = new ProductAttributeLanguageModel($this->view_language);
//$this->objProductAttributeLanguageModel->createTableLang();
parent::__construct($this->objProductAttributeModel, $this->objProductAttributeLanguageModel);
}else{
parent::__construct($this->objProductAttributeModel);
}
}
public function getProductAttributes($product_id) {
$objProductAttributeValueModel = new ProductAttributeValueModel(0);
$result = [];
foreach ($objProductAttributeValueModel->getProductAttributes($product_id) as $info) {
$result[$info['attr_id']][] = $info['attr_value_id'];
}
return $result;
}
public function updateProductAttributes($product_id, array $current_value, array $new_value) {
/*
$current_value => Array
(
// attribute_id => [value1, value2]
[2] => Array
(
[0] => 2
[1] => 3
)
[1] => Array
(
[0] => 1
)
)
$new_value => Array
(
// attribute_id => new_str_values by lines
[2] => asdas
[1] => asda
)
* */
// list of values for product
$product_value_ids = [];
// use current values
foreach ($current_value as $_attr_id => $_value_ids) {
$product_value_ids = array_merge($product_value_ids, $_value_ids);
}
// create new values for attributes
foreach ($new_value as $_attr_id => $_value_text) {
$new_value_texts = array_filter(explode("\n", $_value_text));
foreach ($new_value_texts as $new_value) {
// if exist
$check_exist = $this->getValueByTitle($_attr_id, $new_value);
if($check_exist) {
$product_value_ids[] = $check_exist['id'];
}else{
$try_create_id = $this->addValue($_attr_id, ['title' => $new_value]);
if($try_create_id) $product_value_ids[] = $try_create_id;
}
}
}
$objProductAttributeValueModel = new ProductAttributeValueModel(0);
return $objProductAttributeValueModel->updateProductAttributes($product_id, $product_value_ids);
}
public function updateAttributeInSpecGroup($group_id, $attr_id, array $info) {
$this->objProductAttributeModel->updateAttributeInSpecGroup($group_id, $attr_id, $info);
}
public function removeAttributeFromSpecGroup($group_id, $attr_id) {
$this->objProductAttributeModel->removeAttributeFromSpecGroup($group_id, $attr_id);
}
public function addAttributeToSpecGroup($group_id, $attr_id, $ordering) {
$this->objProductAttributeModel->addAttributeToSpecGroup($group_id, $attr_id, $ordering);
}
public function updateAttributeInCategory($cat_id, $attr_id, array $info) {
$this->objProductAttributeModel->updateAttributeInCategory($cat_id, $attr_id, $info);
}
public function removeAttributeFromCategory($cat_id, $attr_id) {
$this->objProductAttributeModel->removeAttributeFromCategory($cat_id, $attr_id);
}
public function addAttributeToCategory($cat_id, $attr_id, $ordering) {
$this->objProductAttributeModel->addAttributeToCategory($cat_id, $attr_id, $ordering);
}
public function getAllAtributes() {
return $this->objProductAttributeModel->getList(["numPerPage" => 2000]);
}
public function deleteValue($attr_id, $value_id) {
$objProductAttributeValueModel = new ProductAttributeValueModel($attr_id);
return $objProductAttributeValueModel->delete($value_id);
}
public function updateValue($attr_id, $value_id, array $info) {
$objProductAttributeValueModel = new ProductAttributeValueModel($attr_id);
return $objProductAttributeValueModel->updateFields($value_id, $info);
}
public function getValueByTitle($attr_id, $value_title) {
$objProductAttributeValueModel = new ProductAttributeValueModel($attr_id);
$filter_code = DataClean::makeInputSafe($value_title, DataType::ID);
return $objProductAttributeValueModel->getInfoByCode($filter_code);
}
public function addValue($attr_id, array $info) {
$objProductAttributeValueModel = new ProductAttributeValueModel($attr_id);
return $objProductAttributeValueModel->create($info);
}
public function getListAttributeValues($attr_id) {
$objProductAttributeValueModel = new ProductAttributeValueModel($attr_id);
return $objProductAttributeValueModel->getList(['numPerPage' => 200]);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Controller\bProductCategoryController;
use Hura8\Interfaces\AppResponse;
use Hura8\Components\Product\Model\ProductCategoryInfoModel;
use Hura8\Interfaces\iEntityAdminCategoryController;
use Hura8\Traits\AdminEntityCategoryControllerTraits;
class AProductCategoryController extends bProductCategoryController implements iEntityAdminCategoryController
{
use AdminEntityCategoryControllerTraits;
public function updateItemCount($id) {
$this->objProductCategoryModel->updateItemCount($id);
}
public function getAttributeList($catId) {
return $this->objProductCategoryModel->getAttributeList($catId);
}
public function create(array $info) : AppResponse
{
$res = parent::create($info);
if($res->getStatus() == 'ok') {
$objProductCategoryInfoModel = new ProductCategoryInfoModel();
$objProductCategoryInfoModel->createInfo($res->getData(), $info);
}
return $res;
}
public function update($id, array $info) : AppResponse
{
if(!$this->isDefaultLanguage()) {
return parent::update($id, $info);
}
// update info
$objProductCategoryInfoModel = new ProductCategoryInfoModel();
$objProductCategoryInfoModel->updateInfo($id, $info);
return parent::update($id, $info);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Controller\bProductCollectionController;
use Hura8\Interfaces\iEntityAdminCategoryController;
use Hura8\Traits\AdminEntityCategoryControllerTraits;
class AProductCollectionController extends bProductCollectionController implements iEntityAdminCategoryController
{
use AdminEntityCategoryControllerTraits;
public function updateProduct($product_id, $collection_id, array $info)
{
return $this->objProductCollectionModel->updateProduct($product_id, $collection_id, $info);
}
public function removeProduct($product_id, $collection_id)
{
return $this->objProductCollectionModel->removeProduct($product_id, $collection_id);
}
public function addProduct($product_id, $collection_id, $ordering=0)
{
return $this->objProductCollectionModel->addProduct($product_id, $collection_id, $ordering);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Controller\bProductController;
use Hura8\Components\Product\Model\ProductImageModel;
use Hura8\Components\Product\Model\ProductInfoModel;
// Main class extend from base class implementing an interface
// Main class use traits shared by other classes with same responsibilities
class AProductController extends bProductController {
protected function extendFilterConditions(): array
{
return [
"price" => array('max' => 0, 'min'=> 0),
"brand" => array(), // array(1,2,3,)
"collection" => array(), // array(1,2,3,)
"supplier" => array(), // array(1,2,3,)
"rating" => array(), // array(1,2,3,)
"category" => array(), // array(1,2,3,)
"status" => array(), // array(1,2,3,)
"hotType" => array(),// array(saleoff | not | new)
"attribute" => array(), // array(1,2,3,)
"promotion" => "",
"storeId" => array(), // array(1,2,3,)
"other_filter" => array(), // array(in-stock, has-promotion etc...)
"spec_group_id" => array(), // array(1,2,3,)
];
}
//get product image list
public function productImageList($proId){
$objProductImageModel = new ProductImageModel($proId);
$result = array();
foreach ( $objProductImageModel->getList(["numPerPage" => 100]) as $rs ) {
$rs['image'] = static::getResizedImageCollection($rs['img_name']);
$result[] = $rs;
}
return $result;
}
// get only from tb_product_info
public function getInfoMore($id)
{
$objProductInfoModel = new ProductInfoModel();
$info = $objProductInfoModel->getInfo($id);
if(!$this->isDefaultLanguage() && $info) {
$language_info = $this->iEntityLanguageModel->getInfo($id);
$final_info = [];
foreach ($info as $_k => $_v) {
$final_info[$_k] = $language_info[$_k] ?? $_v;
}
return $final_info;
}
return $info;
}
public function getInfoMoreEmpty($addition_field_value = [])
{
$objProductInfoModel = new ProductInfoModel();
return $objProductInfoModel->getEmptyInfo($addition_field_value);
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\System\Url;
use Hura8\Components\Product\Controller\ProductFilterController;
class AProductFilterController extends ProductFilterController
{
public static function getSortOptions($order) {
return array(
array(
"selected" => '',
"name" => "Sắp xếp sản phẩm",
"url" => Url::buildUrl(CURRENT_URL, array("order"=>"")),
),
array(
"selected" => ($order == 'ordering') ? "selected" : "",
"name" => "Thứ tự cửa hàng",
"url" => Url::buildUrl(CURRENT_URL, array("order"=>"ordering")),
),
array(
"selected" => ($order == 'view') ? "selected" : "",
"name" => "Xem nhiều nhất",
"url" => Url::buildUrl(CURRENT_URL, array("order"=>"view")),
),
array(
"selected" => ($order == 'new') ? "selected" : "",
"name" => "Mới nhất",
"url" => Url::buildUrl(CURRENT_URL, array("order"=>"new")),
),
array(
"selected" => ($order == 'last-update') ? "selected" : "",
"name" => "Thời gian cập nhật",
"url" => Url::buildUrl(CURRENT_URL, array("order"=>"last-update")),
),
);
}
public static function getFilterOptions($other_filter) {
return array(
array(
"selected" => ($other_filter == 'no-price') ? "selected" : "",
"name" => "Giá bán = 0",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-price")),
),
array(
"selected" => ($other_filter == 'in-stock') ? "selected" : "",
"name" => "Còn hàng",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"in-stock")),
),
array(
"selected" => ($other_filter == 'out-stock') ? "selected" : "",
"name" => "Hết hàng",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"out-stock")),
),
array(
"selected" => ($other_filter == 'has-market-price') ? "selected" : "",
"name" => "Có giá thị trường",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"has-market-price")),
),
array(
"selected" => ($other_filter == 'has-promotion') ? "selected" : "",
"name" => "Có khuyến mại",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"has-promotion")),
),
array(
"selected" => ($other_filter == 'has-config') ? "selected" : "",
"name" => "Có cấu hình",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"has-config")),
),
array(
"selected" => ($other_filter == 'no-sku') ? "selected" : "",
"name" => "Chưa có mã kho",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-sku")),
),
array(
"selected" => ($other_filter == 'no-image') ? "selected" : "",
"name" => "Chưa có ảnh",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-image")),
),
array(
"selected" => ($other_filter == 'display-off') ? "selected" : "",
"name" => "Chưa hiển thị",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"display-off")),
),
array(
"selected" => ($other_filter == 'display-on') ? "selected" : "",
"name" => "Đang hiển thị",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"display-on")),
),
array(
"selected" => ($other_filter == 'no-category') ? "selected" : "",
"name" => "Chưa có danh mục",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-category")),
),
array(
"selected" => ($other_filter == 'no-brand') ? "selected" : "",
"name" => "Chưa có thương hiệu",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-brand")),
),
array(
"selected" => ($other_filter == 'no-warranty') ? "selected" : "",
"name" => "Chưa có bảo hành",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-warranty")),
),
array(
"selected" => ($other_filter == 'no-description') ? "selected" : "",
"name" => "Chưa có mô tả",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-description")),
),
array(
"selected" => ($other_filter == 'no-spec-text') ? "selected" : "",
"name" => "Chưa có thông số nhập text",
"url" => Url::buildUrl(CURRENT_URL, array("other_filter"=>"no-spec-text")),
),
);
}
}

View File

@@ -0,0 +1,948 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\System\ProductFilterPrice;
use Hura8\System\Registry;
use Hura8\System\Security;
use Hura8\System\Url;
use Hura8\Components\Brand\Model\BrandModel;
use Hura8\Components\Product\Model\ProductAttributeModel;
use Hura8\Components\Product\Model\ProductCategoryModel;
use Hura8\Components\Product\Model\ProductSearchModel;
use Hura8\System\Security\DataType;
use Hura8\Interfaces\TableName;
class AProductFilterOldController
{
protected $filters = [
"price" => false,
"brand" => array(),
"collection" => array(),
"supplier" => array(),
"rating" => array(),
"category" => array(),
"status" => "",
"query" => "",
"hotType" => "",//saleoff | not | new
"attribute" => [],//,3793,3794,
"ids" => array(),
'excluded_ids' => array(),
"promotion" => "",
"storeId" => '',
"other_filter" => array() //in-stock, has-promotion etc...
];
public function __construct() {
}
public function getDefaultFilter() {
return [
"price" => self::getPriceFilterRange(),
"brand" => $this->getBrandFilter(),
"collection" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('collection'))),
"supplier" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('supplier'))),
"rating" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('rating'))),
"category" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('category'))),
//"status" => "1",
"query" => getRequest('q', ''),
"hotType" => getRequest('hotType'),//saleoff | not | new
"attribute" => $this->getAttributeFilter([]),//,3793,3794,
"promotion" => getRequest('promo', ''),
"ids" => array_filter(explode(',', str_replace(" ", "", getRequest('ids', '')))),
'excluded_ids' => [],
"storeId" => getRequest('storeId', ''),
"other_filter" => array_filter(explode(",", getRequest('other_filter', ''))), //in-stock, has-promotion etc...
];
}
// build the user filters for product query
// we can modify the code to accept various types of url customization
public function getUserFilter(array $category_list_id) {
return [
"price" => self::getPriceFilterRange(),
"brand" => $this->getBrandFilter(),
"collection" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('collection'))),
"supplier" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('supplier'))),
"rating" => array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('rating'))),
"category" => $category_list_id,
//"status" => "1",
"query" => getRequest('q', ''),
"hotType" => getRequest('hotType'),//saleoff | not | new
"attribute" => $this->getAttributeFilter( $category_list_id),//,3793,3794,
"promotion" => getRequest('promo', ''),
"ids" => array_filter(explode(',', str_replace(" ", "", getRequest('ids', '')))),
"excluded_ids" => array_filter(explode(',', str_replace(" ", "", getRequest('excluded_ids', '')))),
"storeId" => getRequest('storeId', ''),
"other_filter" => array_filter(explode(",", getRequest('other_filter', ''))), //in-stock, has-promotion etc...
];
}
// allow to update filters
public function setFilters(array $new_filters) {
foreach ($new_filters as $key => $value) {
if(isset($this->filters[$key])) {
$this->filters[$key] = $value;
}
}
}
// accept url format:
// default: ?min=1000&max=20000
// custom: ?p=15trieu-20-trieu
public static function getPriceFilterRange(){
// default format
if(!defined('PRICE_FILTER_FORMAT') || PRICE_FILTER_FORMAT != 'p') {
return array(
"min" => getRequestInt("min", 0),
"max" => getRequestInt("max", 0),
);
}
// custom price range query
$price_range_format = getRequest("p"); // duoi-10trieu , 10ngan-2trieu, 3trieu-6trieu, tren-30trieu
if(strpos($price_range_format, '-') === false) {
return array(
"min" => 0,
"max" => 0,
);
}
$unit_translation = [
'ngan' => 1000,
'trieu' => 1000000,
'ty' => 1000000000,
];
// duoi-10trieu, duoi-10ngan
if(strpos($price_range_format, 'duoi-') !== false) {
$unit_match = self::findPriceUnitMatch(str_replace("duoi-", "", $price_range_format));
return array(
"min" => 0,
"max" => $unit_match['number'] * $unit_translation[$unit_match['unit']],
);
}
// tren-10trieu, tren-10ngan
if(strpos($price_range_format, 'tren-') !== false) {
$unit_match = self::findPriceUnitMatch(str_replace("tren-", "", $price_range_format));
return array(
"min" => $unit_match['number'] * $unit_translation[$unit_match['unit']],
"max" => 0,
);
}
// 10ngan-2trieu, 3trieu-6trieu
$parts = explode('-', $price_range_format);
$min_part = $parts[0];
$max_part = $parts[1];
$min_match = self::findPriceUnitMatch($min_part);
$max_match = self::findPriceUnitMatch($max_part);
return [
"min" => $min_match['number'] * $unit_translation[$min_match['unit']],
"max" => $max_match['number'] * $unit_translation[$max_match['unit']],
];
}
//get a list of products that match filtering conditions
public function getProductList(
$start_url,
$sort_by = "new",
$limit = 20,
$page = 1
) {
//list of available filters
/*
* - price range
* - brand id or ids
* - collection id or ids
* - supplier id
* - rating: 1-> 5
* - category id or ids
* - status: 0|1|null
* - detail_page_only: 0 | 1
* - query: search keyword
* - hotType: saleoff | not | new | or combination of types
* - attribute values
* ...
* */
/*$filters = array(
"price" => array("min"=> 1, "max" => 100),
"brand" => array(12,3),
"collection" => array(2,3),
"supplier" => array(2,3),
"rating" => array(2,3),
"category" => array(1,2,),
"status" => "1",
"detail_page_only" => 0,
"query" => "",
"hotType" => "",//saleoff | not | new
"attribute" => "",//,3793,3794,
"ids" => array(12,3),
'excluded_ids' => array(12,3),
"other_filter" => array(in-stock, has-promotion)
//...
);*/
$filterPath = [];
$filter_messages = [];
$where_query = [];
$paging_url = $start_url;
$filters = $this->cleanFilter();
// debug_var($filters);
//system controls
if(ENABLE_PRODUCT_EXPIRE) {
//$where_query[] = " AND (from_time =0 OR from_time < '".CURRENT_TIME."') AND (to_time > '".CURRENT_TIME."' OR to_time=0 ) ";
}
//other filters
if(isset($filters["other_filter"]) && sizeof($filters["other_filter"])) {
foreach ($filters["other_filter"] as $_filter) {
switch ($_filter) {
case "in-stock";
$filter_messages[] = [
'title' => 'Còn hàng',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND `quantity` > 0 ";
break;
case "has-vat";
$filter_messages[] = [
'title' => 'Có thuế VAT',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND has_vat = 1 ";
break;
case "out-stock";
$filter_messages[] = [
'title' => 'Hết hàng',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND quantity = 0 ";
break;
case "has-market-price";
$filter_messages[] = [
'title' => 'Có giá thị trường',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND market_price > 0 ";
break;
case "no-price";
$filter_messages[] = [
'title' => 'Không có giá',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND price = 0 ";
break;
case "no-warranty";
$filter_messages[] = [
'title' => 'Không có bảo hành',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(`warranty`) < 2 ";
break;
case "no-sku";
$filter_messages[] = [
'title' => 'Không mã kho hàng',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(`sku`) < 2 ";
break;
case "has-sku";
$filter_messages[] = [
'title' => 'Có mã kho hàng',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(`sku`) > 2 ";
break;
case "has-config";
$filter_messages[] = [
'title' => 'Không có cấu hình',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND `config_count` > 0 ";
break;
case "no-image";
$filter_messages[] = [
'title' => 'Không có ảnh',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND `image_count` = 0 ";
break;
case "no-category";
$where_query[] = " AND `category_ids` = '' ";
break;
case "no-brand";
$filter_messages[] = [
'title' => 'Không có thương hiệu',
'reset' => Url::buildUrl(CURRENT_URL, ['brand' => '']),
];
$where_query[] = " AND `brand_id` = 0 ";
break;
case "display-off";
$where_query[] = " AND `status`=0 ";
break;
case "display-on";
$where_query[] = " AND `status`=1 ";
break;
case "has-promotion":
$filter_messages[] = [
'title' => 'Có khuyến mại',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(`special_offer`) > 5 ";
break;
case "no-description";
$filter_messages[] = [
'title' => 'Không có mô tả',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query["no-description"] = " AND `id` IN ( SELECT `id` FROM ".TableName::PRODUCT_INFO." WHERE LENGTH(`description`) < 5 ) ";
break;
case "no-spec-text";
$filter_messages[] = [
'title' => 'Không có thông số',
'reset' => Url::buildUrl(CURRENT_URL, ['other_filter' => '']),
];
$where_query["no-spec-text"] = " AND `id` IN ( SELECT `id` FROM ".TableName::PRODUCT_INFO." WHERE LENGTH(`spec`) < 5 ) ";
break;
}
}
}
//- brand id or ids or brand_indexes
if(isset($filters["brand"]) && sizeof($filters["brand"])) {
global $admin_panel;
$brand_url_format = (defined('BRAND_FILTER_FORMAT')) ? BRAND_FILTER_FORMAT : '';
if(isset($admin_panel) && $admin_panel) $brand_url_format = ''; // custom brand format cannot be used in admin panel
$objBrandModel = new BrandModel();
$condition = array();
foreach ($filters["brand"] as $_id) {
if(!$_id) continue;
$brand_info = $objBrandModel->getInfo($_id);
if(!$brand_info) continue;
$filterPath["brand"][] = array(
"id" => $brand_info['id'],
"name" => $brand_info["title"],
);
$condition[] = " `brand_id` = '".intval($brand_info['id'])."' ";
$filter_messages[] = [
'title' => $brand_info['title'],
'reset' => Url::buildUrl(CURRENT_URL, ['brand' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($filters["brand"], $_id))]),
];
}
if(sizeof($condition)) {
$paging_url = Url::buildUrl($paging_url, array("brand" => join(FILTER_VALUE_SEPARATOR, $filters['brand'])));
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
}
//- collection id or ids
if(isset($filters["collection"]) && sizeof($filters["collection"])) {
$condition = array();
foreach ($filters["collection"] as $_id) {
$filterPath["collection"][] = array(
"id" => $_id,
//"name" => $brand_info["name"],
);
$condition[] = " `id` IN ( SELECT product_id FROM ".TB_CATEGORY_SPECIAL_PRODUCT." WHERE `special_cat_id`='".intval($_id)."' ) ";
$filter_messages[] = [
'title' => 'Bộ sưu tập ',
'reset' => Url::buildUrl(CURRENT_URL, ['collection' => join(',', remove_item_from_array($filters["collection"], $_id))]),
];
}
$paging_url = Url::buildUrl($paging_url, array("collection" => join(",", $filters['collection'])));
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
//- category id or ids
if(isset($filters["category"]) && sizeof($filters["category"])) {
$objCategoryProductModel = new ProductCategoryModel();
$condition = array();
foreach ($filters["category"] as $cat_id) {
$cat_id = intval($cat_id);
if(!$cat_id) continue;
$cat_info = $objCategoryProductModel->getInfo($cat_id);
if($cat_info["is_parent"]) {
$childListId = ($cat_info["child_ids"]) ? $cat_info["child_ids"] : '0';
$condition[] = " `category_id` IN (".$childListId .") ";
}else{
$condition[] = " `category_id` = '".$cat_id."' ";
}
$filterPath["category"][] = array(
"id" => $cat_id,
"name" => $cat_info['title'],
);
$filter_messages[] = [
'title' => $cat_info['title'],
'reset' => Url::buildUrl(CURRENT_URL, ['category' => join(',', remove_item_from_array($filters["category"], $cat_id))]),
];
}
if(sizeof($condition)) {
$paging_url = Url::buildUrl($paging_url, array("category" => join(",", $filters['category'])));
$where_query[] = " AND `id` IN ( SELECT DISTINCT `item_id` FROM ".TableName::PRODUCT_PER_CATEGORY." WHERE " . join(" OR ", $condition) . " )";
}
}
//- status: 0|1|null
if(isset($filters["status"]) && $filters['status']) {
$where_query[] = " AND `status` = 1 ";
/*$filter_messages[] = [
'title' => 'Trạng thái: '.($filters['status'] ? 'Đang hiển thị' : 'Đang ẩn'),
'reset' => Url::buildUrl(CURRENT_URL, ['status' => '']),
];*/
}
//- query: search keyword
if(isset($filters["query"]) && $filters["query"]) {
$keyword_search = $filters["query"];
$search_by_product_id = intval(preg_replace('/[^0-9]/i', '', $keyword_search));
$objProductSearchModel = new ProductSearchModel();
$match_result = $objProductSearchModel->find($keyword_search);
$filterPath["search"] = array(
"id" => $filters["query"],
"name" => $filters["query"],
);
$filter_messages[] = [
'title' => $filters["query"],
'reset' => Url::buildUrl(CURRENT_URL, ['q' => '']),
];
$paging_url = Url::buildUrl($paging_url, array("q" => $filters["query"]));
if(sizeof($match_result) > 0) {
$where_query[] = " AND ( `id` IN (".join(",", $match_result ).") OR `id` = '".$search_by_product_id."' ) ";
}else{
$where_query[] = " AND `id` = '".$search_by_product_id."' ";
}
}
//- hotType: saleoff | not | new | or combination of types
if(isset($filters["hotType"]) && $filters["hotType"]) {
$hot_type = preg_replace("/[^a-z0-9_\-]/i","", $filters["hotType"]);
$config_hottype = AProductFilterController::getProductHotTypeList();
if(isset($config_hottype[$hot_type])) {
$filterPath["hotType"] = array(
"id" => $filters["hotType"],
"name" => $filters["hotType"],
);
$paging_url = Url::buildUrl($paging_url, array("hotType" => $hot_type));
$where_query[] = " AND `id` IN (SELECT `pro_id` FROM ".TB_PRODUCT_HOT." WHERE hot_type = '".$hot_type."' ) ";
$filter_messages[] = [
'title' => $filters["hotType"],
'reset' => Url::buildUrl(CURRENT_URL, ['hotType' => '']),
];
}
}
//- attribute values
if(isset($filters["attribute"]) && sizeof($filters["attribute"])) {
$filter_attr_value_list = $filters["attribute"];
//filter = attr_value_1-attr_value_2-attr_value_3,
$query_attr_id = [];
$count_filter = 0;
if(ENABLE_FILTER_BY_APIKEY) {
//filter = ,api_key_1,api_key_2,api_key_3,
foreach($this->translate_api_filter($filter_attr_value_list) as $attr_id){
$query_attr_id[] = $attr_id;
$count_filter ++;
}
} else {
foreach($filter_attr_value_list as $attr_id){
$attr_id = (int) $attr_id;
if($attr_id) {
$query_attr_id[] = $attr_id;
$count_filter ++;
}
}
}
$objProductAttributeModel = new ProductAttributeModel();
$product_filter_id_match = $objProductAttributeModel->getProductMatchAttributeValue($query_attr_id);
$paging_url = Url::buildUrl($paging_url, array("filter" => join(FILTER_VALUE_SEPARATOR, $filter_attr_value_list)));
$where_query[] = (sizeof($product_filter_id_match)) ? " AND `id` IN (".join(', ', $product_filter_id_match).") " : " AND `id` = 0 " ;
//xay lai url de back
foreach($filter_attr_value_list as $value_id ){
$att_name = 'att_name'; //$objCategoryProduct->atrValueName($value_id);
$filterPath["attribute"][] = array(
"id" => $value_id,
"name" => $att_name,
);
$filter_messages[] = [
'title' => $att_name,
'reset' => Url::buildUrl(CURRENT_URL, ['attribute' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($filters["category"], $value_id))]),
];
}
}
//given products' ids
if(isset($filters["ids"]) && is_array($filters["ids"]) && sizeof($filters["ids"])) {
$where_query[] = " AND `id` IN (". join(",", $filters["ids"]) .") ";
}
//exclude products' ids
if(isset($filters["excluded_ids"]) && is_array($filters["excluded_ids"]) && sizeof($filters["excluded_ids"])) {
$where_query[] = " AND `id` NOT IN (". join(",", $filters["excluded_ids"]) .") ";
}
$price_range_query_limit = join(" ",$where_query);
//- price range
if(isset($filters["price"]) && is_array($filters["price"]) && sizeof($filters["price"]) && ($filters["price"]['min'] > 0 || $filters["price"]['max'] > 0)) {
//limit by price range
$maxPrice = clean_price($filters["price"]['max']);
$minPrice = clean_price($filters["price"]['min']);
$paging_url = Url::buildUrl($paging_url, array("max"=>$maxPrice, "min"=>$minPrice));
$price_range_query = '';
if($maxPrice > 0 && $minPrice > 0){
$price_range_query = " ( `price` BETWEEN '".$minPrice."' AND '".$maxPrice."' ) ";
}else if($maxPrice > 0){
$price_range_query = " `price` < '".$maxPrice."' ";
}else if($minPrice > 0){
$price_range_query = " `price` >='".$minPrice."' ";
}
$where_query[] = " AND ". $price_range_query;
$filterPath["price"] = array(
"min" => $minPrice,
"max" => $maxPrice,
);
$price_format = ProductFilterPrice::buildPriceRangeFormat($minPrice, $maxPrice);
$filter_messages[] = [
'title' => $price_format['title'],
'reset' => Url::buildUrl(CURRENT_URL, ['price' => '']),
];
}
// use location price sorting
$ordering_clause = $this->getOrderingClause($sort_by, $db_condition, 0);
$db_condition = join(" ", $where_query);
$total_number = 0;
$list_ids = [];
/* $query = $this->db->runQuery("SELECT COUNT(`id`) AS total FROM ".TB_PRODUCT_LIGHT." WHERE 1 ".$db_condition." ");
if($rs = $this->db->fetchAssoc($query)) {
$total_number = $rs['total'];
}
$category_query = " AND idv_product_category.`pro_id` IN (SELECT `id` FROM ".TB_PRODUCT_LIGHT." WHERE 1 ".$db_condition.") ";
if($location_sorting) {
$list_ids = array_slice($location_product_ids, ($page-1) * $limit, $limit);
} else {
if(in_array($sort_by, ['view', 'name'])) {
$query = $this->db->runQuery("
SELECT `id`, ".TB_PRODUCT.".proName
FROM ".TB_PRODUCT_LIGHT.", ".TB_PRODUCT."
WHERE `id` = ".TB_PRODUCT.".`id`
".$db_condition."
".$ordering_clause."
LIMIT ".($page-1) * $limit.", ".$limit."
");
}else{
$query = $this->db->runQuery("
SELECT `id` FROM ".TB_PRODUCT_LIGHT."
WHERE 1 ".$db_condition."
".$ordering_clause."
LIMIT ".($page-1) * $limit.", ".$limit."
");
}
$list_ids = array_map(function ($rs){
return $rs['id'];
}, $this->db->fetchAll($query));
}*/
return [
"full_query" => $db_condition,
"price_query" => $price_range_query_limit,
//"category_query" => $category_query,
"filter" => $filterPath,
"filter_messages" => $filter_messages,
"total" => $total_number,
"list_ids" => $list_ids,
"url" => $paging_url,
];
}
protected function cleanFilter() {
$clean_filter = [];
foreach ($this->filters as $key => $value) {
$clean_filter[$key] = $this->clearFilterValue($key, $value);
}
return $clean_filter;
}
protected function clearFilterValue($key, $value) {
if($key == 'price') {
if(!is_array($value)) return false;
$min = isset($value['min']) ? intval($value['min']) : 0;
$max = isset($value['max']) ? intval($value['max']) : 0;
return ['min' => $min, 'max' => $max]; // array("min" => getRequestInt("min", 0), "max" => getRequestInt("max", 0)),
}
if(in_array($key, ['collection', 'supplier', 'rating', 'category', 'ids', 'excluded_ids'])) {
return Security::makeListOfInputSafe($value, 'int'); // int1-int2 => array(int1, int2, ...)
}
// accept a-z0-9-
if(in_array($key, ['attribute', 'brand'])) {
return Security::makeListOfInputSafe($value, 'string');
}
if($key == 'status') {
return (in_array($value, [0, 1])) ? $value : 0;
}
if($key == 'query') {
return substr(trim(str_replace(['\'', '"'], '', strip_tags($value))), 0, 100); // string
}
if($key == 'hotType') {
$config_hottype = AProductFilterController::getProductHotTypeList();
return (isset($config_hottype[$value])) ? $value : ''; //saleoff | not | new
}
if($key == 'promotion') {
return intval($value);
}
if($key == 'storeId') {
return intval($value);
}
if($key == 'detail_page_only') {
return intval($value); // 1|0|''
}
if($key == 'other_filter') {
return array_filter(
array_map(function ($item){
return preg_replace('/[^a-z0-9_\.\-\,]/i', '', $item);
}, $value )
); // array() //in-stock, has-promotion
}
return false;
}
protected function filterLocationProductByPrice(array $product_list, $max_price, $min_price) {
if($max_price > 0 && $min_price > 0){
return array_filter($product_list, function ($pro) use ($max_price, $min_price) {
return ($pro['price'] >= $min_price && $pro['price'] < $max_price);
});
}
if($max_price > 0){
return array_filter($product_list, function ($pro) use ($max_price, $min_price) {
return ($pro['price'] < $max_price);
});
}
if($min_price > 0){
return array_filter($product_list, function ($pro) use ($max_price, $min_price) {
return ($pro['price'] >= $min_price);
});
}
return $product_list;
}
protected function getProductIds($price_range_query_limit) {
/*$query = $this->db->query("
SELECT `id` FROM ".TB_PRODUCT_LIGHT."
WHERE 1 ".$price_range_query_limit."
LIMIT 10000
");
return array_map(function ($rs){
return $rs['id'];
}, $this->db->fetchAll($query));*/
}
//10-04-2017 allow ?filter=api_key1,api_key2, ... and translate to id
protected function translate_api_filter($filter_api) {
/*$filter_attr_value_list = array_filter(explode(",", $filter_api));
$build_query = array();
foreach($filter_attr_value_list as $api_key){
$api_key = \preg_replace('/[^a-z0-9\_\-\.]/i', '', $api_key);
if($api_key) $build_query[] = " `api_key` = '".$this->db->escape($api_key)."' ";
}
$result = array();
if(sizeof($build_query)) {
$query = $this->db->runQuery("SELECT id FROM ".TB_ATTRIBUTE_VALUE." WHERE ". join(" OR ", $build_query)) ;
foreach ( $this->db->fetchAll($query) as $info ) {
$result[] = $info['id'];
}
}
return $result;*/
return '';
}
// accept url format:
// default: ?brand=1
// brand_index: ?brand=apple
protected function getBrandFilter() {
$brand_url_format = (defined('BRAND_FILTER_FORMAT')) ? BRAND_FILTER_FORMAT : '';
$module = (Registry::getVariable('global')) ? Registry::getVariable('global')['module'] : null;
if(!$module) {
// possible admin panel
return array_filter(explode(FILTER_VALUE_SEPARATOR, getRequest('brand', '')));
}
$current_brand_queries = (isset($module['query']['brand'])) ? array_filter(explode(FILTER_VALUE_SEPARATOR, $module['query']['brand'])) : [];
// fix for brand-detail page
// example: domain.com/brand/sony
if(isset($module['query']['brandName']) && $module['query']['brandName']) {
$objBrandModel = new BrandModel();
$brand_info = $objBrandModel->getInfoByUrl($module['query']['brandName']);
if($brand_url_format == 'brand_index') {
$current_brand_queries = array($brand_info['brand_index']);
}else{
$current_brand_queries = array($brand_info['id']);
}
}
if($brand_url_format == 'brand_index') {
//i.e ?brand=apple-canon
return $current_brand_queries;
}
$selected_brand = $current_brand_queries;
if(isset($module['query']['brand_id']) && $module['query']['brand_id']) {
$selected_brand[] = intval($module['query']['brand_id']);
}
return $selected_brand;
}
// accept url format:
// default: ?filter=3793-3794,
// custom: ?ram=8gb&hdd=500GB
protected function getAttributeFilter(array $category_list_id){
$request_filter_list = getRequest('filter', '');
// custom1
if( !$request_filter_list && defined('ATTRIBUTE_FILTER_FORMAT') && ATTRIBUTE_FILTER_FORMAT == 'custom1' ) {
// iUCategoryProduct $objCategoryProduct, array $category_list_id
return $this->getAttributeFilterPara($category_list_id); //join(FILTER_VALUE_SEPARATOR, $this->getAttributeFilterPara($category_list_id));
}
if(!$request_filter_list) {
return [];
}
// default
$clean_list = Security::makeListOfInputSafe(
explode(FILTER_VALUE_SEPARATOR, str_replace([',', '-', '_'], FILTER_VALUE_SEPARATOR, $request_filter_list) ),
DataType::INTEGER
) ; //preg_replace("/[^0-9,_\-]/", "", getRequest('filter', ''));//3793-3794,
return array_filter($clean_list);
}
protected function getAttributeFilterPara( array $category_list_id){
$excluded_keys = ['p', 'min', 'max', 'brand', 'page', 'request_path'];
$attr_filter_params = [];
$query_parameters = Url::parse(CURRENT_URL)['query'];
foreach ( $query_parameters as $key => $value) {
if(in_array($key, $excluded_keys) || !$value) continue;
$attr_filter_params[$key] = $value;
}
if(sizeof($attr_filter_params)) {
/*$objCategoryProduct = new UCategoryProduct();
$category_attributes = $objCategoryProduct->getAttributesForCategory($category_list_id);
$match_attr_value_ids = [];
foreach ($attr_filter_params as $attr_filter_code => $attr_value_api_key) {
foreach ($category_attributes as $attribute) {
if($attribute['filter_code'] != $attr_filter_code) continue;
foreach ($attribute['value_list'] as $_value_id => $_value_info) {
if($_value_info['info']['api_key'] == $attr_value_api_key) {
$match_attr_value_ids[] = $_value_id;
}
}
}
}
return $match_attr_value_ids;*/
return [];
}
return [];
}
public static function findPriceUnitMatch($str){
$match = [];
$pattern = "/^([\d]+)(ngan|trieu|ty)/i";
if(preg_match($pattern, $str, $match)){
return [
"number" => $match[1],
"unit" => $match[2],
];
}
return [
"number" => 0,
"unit" => 'trieu',
];
}
// accept url format:
// default: ?min=1000&max=20000
// custom: ?p=tu-15-20-trieu
protected static function getPriceRange(){
// default
$min_price = getRequest("min");
$max_price = getRequest("max");
// custom price range query
$price_range = getRequest("p"); // duoi-10-trieu , tu-15-20-trieu, tren-30-trieu
// duoi-10-trieu
$match = [];
if(preg_match("/^duoi-([0-9]+)-trieu$/i", $price_range, $match)) {
$min_price = '';
$max_price = $match[1] * 1000000;
}
elseif (preg_match("/^tu-([0-9]+)-([0-9]+)-trieu$/i", $price_range, $match)) {
$min_price = $match[1] * 1000000;
$max_price = $match[2] * 1000000;
}
elseif (preg_match("/^tren-([0-9]+)-trieu$/i", $price_range, $match)) {
$min_price = $match[1] * 1000000;
$max_price = '';
}
return [
"min" => intval($min_price),
"max" => intval($max_price),
];
}
protected function getOrderingClause($sort_by, &$where_query, $current_viewed_category = 0) {
// show by global order
$ordering = "ORDER BY ordering DESC, id DESC";
switch($sort_by) {
case "order-new";
$ordering = "ORDER BY `ordering` DESC, `id` DESC";
break;
case "order-last-update";
$ordering = "ORDER BY `ordering` DESC, last_update DESC";
break;
case "last-update";
$ordering = "ORDER BY last_update DESC";
break;
case "order";
$ordering = "ORDER BY `ordering` DESC";
break;
case "new";
$ordering = "ORDER BY `id` DESC";
break;
case "price-asc";
$where_query .= " AND `price` > 100 ";
$ordering = "ORDER BY `price` ASC";
break;
case "price-desc";
$ordering = " ORDER BY `price` DESC ";
break;
case "view";
$ordering = "ORDER BY `visit` desc";
break;
}
return $ordering;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Model\ProductHotModel;
use Hura8\Traits\ClassCacheTrait;
class AProductHotController
{
use ClassCacheTrait;
/* @var ProductHotModel $objProductHotModel */
protected $objProductHotModel;
public function __construct()
{
$this->objProductHotModel = new ProductHotModel();
}
public function getProductHot(array $product_id_array) {
$hot_script = $this->objProductHotModel->getProductHot($product_id_array);
//add empty for other products
foreach ($product_id_array as $pro_id) {
if(!isset($hot_script[$pro_id])) $hot_script[$pro_id] = [];
}
return $hot_script;
}
public function updateProductHot($pro_id, array $new_types) {
return $this->objProductHotModel->updateProductHot($pro_id, $new_types);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Model\ProductSpecGroupAttributeModel;
use Hura8\System\Controller\aAdminEntityBaseController;
class AProductSpecGroupAttributeController extends aAdminEntityBaseController
{
/* @var ProductSpecGroupAttributeModel $objProductSpecGroupAttributeModel */
protected $objProductSpecGroupAttributeModel;
public function __construct()
{
$this->objProductSpecGroupAttributeModel = new ProductSpecGroupAttributeModel();
parent::__construct($this->objProductSpecGroupAttributeModel);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Model\ProductAttributeModel;
use Hura8\Components\Product\Model\ProductSpecGroupModel;
use Hura8\System\Controller\aAdminEntityBaseController;
class AProductSpecGroupController extends aAdminEntityBaseController
{
/* @var ProductSpecGroupModel $objProductSpecGroupModel */
protected $objProductSpecGroupModel;
public function __construct()
{
$this->objProductSpecGroupModel = new ProductSpecGroupModel();
parent::__construct($this->objProductSpecGroupModel);
}
public function getSpecGroupAttributeWithValues($group_id)
{
$objProductAttributeModel = new ProductAttributeModel();
return $objProductAttributeModel->getSpecGroupAttributeWithValues($group_id);
}
public function getSpecGroupAttribute($group_id)
{
$objProductAttributeModel = new ProductAttributeModel();
return $objProductAttributeModel->getSpecGroupAttribute($group_id);
}
public function clearProductSpecGroup($product_id)
{
return $this->objProductSpecGroupModel->clearProductSpecGroup($product_id);
}
public function setProductSpecGroup($product_id, $group_id)
{
return $this->objProductSpecGroupModel->setProductSpecGroup($product_id, $group_id);
}
public function getProductSpec($product_id)
{
return $this->objProductSpecGroupModel->getProductSpec($product_id);
}
public function getProductSpecGroupInfo($product_id)
{
return $this->objProductSpecGroupModel->getProductSpecGroupInfo($product_id);
}
public function getProductSpecGroupId($product_id)
{
return $this->objProductSpecGroupModel->getProductSpecGroupId($product_id);
}
public function getSpecGroupInfo($group_id)
{
return $this->objProductSpecGroupModel->getSpecGroupInfo($group_id);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Model\ProductVariantModel;
class AProductVariantController
{
/* @var ProductVariantModel $objProductVariantModel */
protected $objProductVariantModel;
public function __construct($product_id)
{
$this->objProductVariantModel = new ProductVariantModel($product_id);
}
public function delete($id) {
return $this->objProductVariantModel->delete($id);
}
public function updateImage($variant_id, $image_name) {
return $this->objProductVariantModel->updateFields($variant_id, [
"thumbnail" => $image_name
]);
}
public function removeImage($variant_id) {
return $this->objProductVariantModel->updateFields($variant_id, [
"thumbnail" => ''
]);
}
public function getInfo($id) {
return $this->objProductVariantModel->getInfo($id);
}
public function getProductVariantOption($product_id){
return $this->objProductVariantModel->getProductVariantOption($product_id);
}
//update product's variant options
public function updateVariantOption($attribute) {
return $this->objProductVariantModel->updateVariantOption($attribute);
}
public function useVariantOptionSample($select_id) {
$sample = $this->objProductVariantModel->useVariantOptionSample($select_id);
return ($sample) ? \json_decode($sample,true) : [];
}
//use a product's variant-option to create a choice, so next product can select without recreate from beginning
public function createVariantOptionSample($use_from_pro_id, $sample_title) {
return $this->objProductVariantModel->createVariantOptionSample($use_from_pro_id, $sample_title);
}
public function getVariantOptionSample() {
return $this->objProductVariantModel->getVariantOptionSample();
}
public function updateVariant($variant_id, array $variant_info) {
if($variant_id) {
return $this->objProductVariantModel->update($variant_id, $variant_info);
}else{
return $this->objProductVariantModel->create($variant_info);
}
}
public function getProductVariantPriceRange(){
return $this->objProductVariantModel->getProductVariantPriceRange();
}
public function getProductVariantList(){
return $this->objProductVariantModel->getList([]);
}
}

View File

@@ -0,0 +1,320 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\System\Url;
use Hura8\Components\Brand\Model\BrandModel;
class ProductFilterBuilderController {
protected $_request_url = '';
protected $_url_elements = [
'scheme' => '',
'host' => '',
'port' => '',
'user' => '',
'pass' => '',
'path' => '',
'query' => [],
'fragment' => '',
];
protected $_price_format = '';
protected $_brand_format = '';
protected $_filter_format = '';
public function __construct($request_url) {
if(defined('PRICE_FILTER_FORMAT') && PRICE_FILTER_FORMAT) {
$this->_price_format = PRICE_FILTER_FORMAT;
}
if(defined('BRAND_FILTER_FORMAT') && BRAND_FILTER_FORMAT) {
$this->_brand_format = BRAND_FILTER_FORMAT;
}
if(defined('ATTRIBUTE_FILTER_FORMAT') && ATTRIBUTE_FILTER_FORMAT ) {
$this->_filter_format = ATTRIBUTE_FILTER_FORMAT;
}
$this->_request_url = $request_url;
$this->_url_elements = Url::parse($request_url);
}
public function buildProductFilterFromUrl(array $existing_filters = []) {
$filters = [
"price" => $this->getPriceFilterRange(),
"brand" => $this->getBrandFilter(), // array(1,2,3,)
"collection" => $this->getGenericFilter('collection'), // array(1,2,3,)
"supplier" => $this->getGenericFilter('supplier'), // array(1,2,3,)
"rating" => $this->getGenericFilter('rating'), // array(1,2,3,)
"category" => $this->getGenericFilter('category'), // array(1,2,3,)
"status" => array(), // array(1,2,3,)
"query" => (isset($this->_url_elements['query']['q'])) ? $this->_url_elements['query']['q'] : '', // string search keyword
"hotType" => $this->getGenericFilter('hotType', 'string'),// array(saleoff | not | new)
"attribute" => $this->getAttributeFilter(), // array(1,2,3,)
"ids" => $this->getGenericFilter('ids'), // array(1,2,3,)
"promotion" => "",
"storeId" => $this->getGenericFilter('store'), // array(1,2,3,)
"other_filter" => $this->getGenericFilter('other_filter', 'string'), // array(in-stock, has-promotion etc...)
"spec_group_id" => $this->getGenericFilter('spec_group_id'),
];
// debug_var(array_merge($filters, $existing_filters));
if(sizeof($existing_filters)) {
foreach ($existing_filters as $key => $values) {
if(!isset($filters[$key])) continue;
if(!$values) continue;
if(is_array($values) && is_array($filters[$key])) {
$filters[$key] = array_merge($filters[$key], $values);
}else{
$filters[$key] = $values;
}
}
}
return $filters ;
}
protected function getGenericFilter($url_para, $data_type='int') {
$value = (isset($this->_url_elements['query'][$url_para])) ? $this->_url_elements['query'][$url_para] : '';
if($data_type == 'int') {
$safe_value = trim(preg_replace("/[^0-9_\-\,]/i", '', $value));
}else{
$safe_value = trim(preg_replace("/[^a-z0-9_\-\.\,]/i", '', $value));
}
if(!$safe_value) return [];
$safe_value_list = explode(FILTER_VALUE_SEPARATOR, $safe_value);
return array_values(array_filter($safe_value_list));
}
protected function getBrandFilter()
{
if($this->_brand_format == 'brand_index'){
$objBrandModel = new BrandModel();
$result = [];
$brand_filters = $this->getGenericFilter('brand', 'string');
foreach ($brand_filters as $_brand) {
if(is_int($_brand)) {
$result[] = $_brand;
}
$brand_info = $objBrandModel->getInfoByUrl( $_brand);
if(!$brand_info) continue;
$result[] = $brand_info['id'];
}
return $result;
}
// default
return $this->getGenericFilter('brand');
}
protected function getAttributeFilter(array $category_list_id = []) {
if($this->_filter_format == 'filter_code') {
$excluded_keys = [
'q',
'p',
'min', 'max',
'brand',
'page',
'request_path',
'category',
'supplier', 'collection',
'ids',
'filter',' hotType',
'other_filter',
'rating', 'price',
// special ajax key
'action','action_type', '_auto'
];
$attr_filter_params = [];
foreach ( $this->_url_elements['query'] as $key => $value) {
if(in_array($key, $excluded_keys) || !$value) continue;
$attr_filter_params[$key] = explode(',', urldecode(urldecode($value)));
}
if(sizeof($attr_filter_params)) {
$category_attributes = $this->getAttributesForCategory($category_list_id);
//debug_var($category_attributes);
$match_attr_value_ids = [];
foreach ($attr_filter_params as $attr_filter_code => $attr_value_api_key_list) {
foreach ($category_attributes as $attribute) {
if($attribute['filter_code'] != $attr_filter_code) continue;
foreach ($attribute['value_list'] as $_index => $_value_info) {
foreach ($attr_value_api_key_list as $attr_value_api_key) {
if($_value_info['api_key'] == $attr_value_api_key) {
$match_attr_value_ids[] = $_value_info['id'];
}
}
}
}
}
return $match_attr_value_ids;
}
}
// default
return $this->getGenericFilter('filter');
}
protected function getAttributesForCategory(array $category_ids){
$category_att_id_list = array();
$attr_query_cond = (sizeof($category_ids)) ? " AND category_id IN (".join(',', $category_ids).") " : "";
$query = $this->db->runQuery(" SELECT `attr_id` FROM `idv_attribute_category` WHERE 1 ". $attr_query_cond);
foreach ($this->db->fetchAll($query) as $rs ){
$category_att_id_list[] = $rs['attr_id'];
}
if(!sizeof($category_att_id_list)) return array();
$query = $this->db->query("
SELECT
attval.id ,
attval.attributeId ,
attval.value ,
attval.api_key ,
att.attribute_code ,
att.filter_code
FROM ".TB_ATTRIBUTE_VALUE." attval
LEFT JOIN ".TB_ATTRIBUTE." att ON attval.attributeId = att.id
WHERE att.id IN (".join(',', $category_att_id_list).") AND att.isSearch=1
ORDER BY attval.ordering DESC ");
$attribute_list = [];
foreach ($this->db->fetchAll($query) as $rs ){
$att_value_id = $rs['id'];
$att_id = $rs['attributeId'];
$value_info = array(
"id" => $att_value_id,
"api_key" => $rs['api_key'],
);
if(!array_key_exists($att_id, $attribute_list)) {
$attribute_list[$att_id] = [
"id" => $att_id,
"code" => $rs['attribute_code'],
"filter_code" => $rs['filter_code'],
'value_list' => [$value_info],
];
}else{
$attribute_list[$att_id]['value_list'][] = $value_info;
}
}
return array_values($attribute_list);
}
// accept url format:
// default: ?min=1000&max=20000
// custom: ?p=15trieu-20-trieu
protected function getPriceFilterRange(){
// default format
if($this->_price_format != 'p') {
return array(
"min" => isset($this->_url_elements['query']['min']) ? intval($this->_url_elements['query']['min']) : 0 ,
"max" => isset($this->_url_elements['query']['max']) ? intval($this->_url_elements['query']['max']) : 0 ,
);
}
// custom price range query
$price_range_format = (isset($this->_url_elements['query']['p'])) ? $this->_url_elements['query']['p'] : ''; // duoi-10trieu , 10ngan-2trieu, 3trieu-6trieu, tren-30trieu
if(strpos($price_range_format, '-') === false) {
return array(
"min" => 0,
"max" => 0,
);
}
$unit_translation = [
'ngan' => 1000,
'trieu' => 1000000,
'ty' => 1000000000,
];
// duoi-10trieu, duoi-10ngan
if(strpos($price_range_format, 'duoi-') !== false) {
$unit_match = $this->findPriceUnitMatch(str_replace("duoi-", "", $price_range_format));
return array(
"min" => 0,
"max" => $unit_match['number'] * $unit_translation[$unit_match['unit']],
);
}
// tren-10trieu, tren-10ngan
if(strpos($price_range_format, 'tren-') !== false) {
$unit_match = $this->findPriceUnitMatch(str_replace("tren-", "", $price_range_format));
return array(
"min" => $unit_match['number'] * $unit_translation[$unit_match['unit']],
"max" => 0,
);
}
// 10ngan-2trieu, 3trieu-6trieu
$parts = explode('-', $price_range_format);
$min_part = $parts[0];
$max_part = $parts[1];
$min_match = $this->findPriceUnitMatch($min_part);
$max_match = $this->findPriceUnitMatch($max_part);
return [
"min" => $min_match['number'] * $unit_translation[$min_match['unit']],
"max" => $max_match['number'] * $unit_translation[$max_match['unit']],
];
}
protected function findPriceUnitMatch($str){
$match = [];
$pattern = "/^([\d]+)(ngan|trieu|ty)/i";
if(preg_match($pattern, $str, $match)){
return [
"number" => $match[1],
"unit" => $match[2],
];
}
return [
"number" => 0,
"unit" => 'trieu',
];
}
}

View File

@@ -0,0 +1,797 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\Components\Product\Model\ProductCollectionModel;
use Hura8\System\Url;
use Hura8\Components\Brand\Model\BrandModel;
use Hura8\Components\Product\AdminController\AProductHotController;
use Hura8\Components\Product\Model\ProductAttributeModel;
use Hura8\Components\Product\Model\ProductAttributeValueModel;
use Hura8\Components\Product\Model\ProductCategoryModel;
use Hura8\Components\Product\Model\ProductFilterModel;
use Hura8\Components\Product\Model\ProductSearchModel;
use Hura8\Interfaces\TableName;
use Hura8\Traits\ClassCacheTrait;
class ProductFilterController
{
use ClassCacheTrait;
protected $_request_url = '';
/* @var ProductFilterModel $objProductFilterModel */
protected $objProductFilterModel;
/*
all types of filtering representation must be finally translated to this format
For example:
- ?brand=sony => translated to brand=>array(2,),
- ?monitor_size=12inch&color=red => translated to attribute=> array(123, 23121,)
*/
protected $filters = [
"price" => array('max' => 0, 'min'=> 0),
"brand" => array(), // array(1,2,3,)
"collection" => array(), // array(1,2,3,)
"supplier" => array(), // array(1,2,3,)
"rating" => array(), // array(1,2,3,)
"category" => array(), // array(1,2,3,)
"status" => array(), // array(1,2,3,)
"query" => "", // string search keyword
"hotType" => array(),// array(saleoff | not | new)
"attribute" => array(), // array(1,2,3,)
"ids" => array(), // array(1,2,3,)
"excluded_ids" => array(), // array(1,2,3,)
"promotion" => "",
"storeId" => array(), // array(1,2,3,)
"other_filter" => array(), // array(in-stock, has-promotion etc...)
"spec_group_id" => array(), // array(1,2,3,)
];
public function __construct($request_url = '') {
$this->_request_url = $request_url;
}
public function getResult(
$current_page_id =1,
$number_per_page=10,
$sort_by = 'new',
array $existing_filters=[],
$current_category_ids = [],
$price_range = []
) {
$objProductFilterBuilder = new ProductFilterBuilderController($this->_request_url);
$built_filters = $objProductFilterBuilder->buildProductFilterFromUrl($existing_filters);
$this->setFilters($built_filters);
$product_result = $this->findProductListIdMatchFilters($current_page_id, $number_per_page, $sort_by);
//debug_var($product_result);
$objProductFilterOptions = new ProductFilterOptionsController(
$this->getFilters(),
$current_category_ids,
$product_result['item_list'],
$price_range
);
$objProductFilterOptionsTranslation = new ProductFilterOptionsTranslationController($this->_request_url);
list ( $filter_options, $filter_messages ) = $objProductFilterOptionsTranslation->getAllFilterOptions(
$objProductFilterOptions->categoryList(),
$objProductFilterOptions->attributeList(),
$objProductFilterOptions->brandList(),
$objProductFilterOptions->priceList()
);
return [
'total' => $product_result['total'],
'paged_product_list' => $product_result['item_list'],
'filter_options' => $filter_options,
'filter_messages' => $filter_messages,
];
}
public function getFilters() {
return $this->filters;
}
// allow to update filters
public function setFilters(array $new_filters) {
foreach ($new_filters as $key => $value) {
if(isset($this->filters[$key])) {
$this->filters[$key] = $value;
}
}
}
protected function createFilterCacheKey($sort_by) {
return md5(\json_encode($this->filters) . $sort_by);
}
// find a list of products that match filtering conditions
protected function findProductListIdMatchFilters($current_page_id, $number_per_page, $sort_by = "new" ) {
$cache_key = $this->createFilterCacheKey($sort_by);
return self::getCache($cache_key, function () use ($current_page_id, $number_per_page, $sort_by) {
return $this->findProductListIdMatchFilters_raw($current_page_id, $number_per_page, $sort_by );
});
}
protected function findProductListIdMatchFilters_raw($current_page_id=1, $number_per_page=10, $sort_by = "new" ) {
list( , , , $where_query ) = $this->buildSQLConditionFromFilters($sort_by);
$ordering_clause = $this->getOrderingClause($sort_by);
$objProductFilterModel = new ProductFilterModel();
list($total_number, $item_list) = $objProductFilterModel->findProductListIdMatchFilters([
$where_query,
$current_page_id,
$number_per_page,
$ordering_clause
]);
$final_result = [
"total" => $total_number,
"item_list" => $item_list,
];
return $final_result;
}
protected function buildSQLConditionFromFilters( $sort_by = "new" ) {
//list of available filters
$filterPath = [];
$filter_messages = [];
$where_query = [];
// system controls
if( ENABLE_PRODUCT_EXPIRE ) {
$where_query[] = " AND (from_time =0 OR from_time < '".CURRENT_TIME."')
AND (to_time > '".CURRENT_TIME."' OR to_time=0 ) ";
}
//other filters
if( array_key_exists("other_filter", $this->filters) && sizeof($this->filters["other_filter"])) {
foreach ($this->filters["other_filter"] as $_filter) {
switch ($_filter) {
case "in-stock";
$filter_messages[] = [
'title' => 'Còn hàng',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND quantity > 0 ";
break;
case "has-vat";
$filter_messages[] = [
'title' => 'Có thuế VAT',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `has_vat` = 1 ";
break;
case "out-stock";
$filter_messages[] = [
'title' => 'Hết hàng',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `quantity` = 0 ";
break;
case "has-market-price";
$filter_messages[] = [
'title' => 'Có giá thị trường',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND market_price > 0 ";
break;
case "no-price";
$filter_messages[] = [
'title' => 'Không có giá',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `price` = 0 ";
break;
case "no-warranty";
$filter_messages[] = [
'title' => 'Không có bảo hành',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(warranty) < 2 ";
break;
case "no-sku";
$filter_messages[] = [
'title' => 'Không mã kho hàng',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(sku) < 2 ";
break;
case "has-config";
$filter_messages[] = [
'title' => 'Không có cấu hình',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `config_count` > 0 ";
break;
case "no-image";
$filter_messages[] = [
'title' => 'Không có ảnh',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND image_count = 0 ";
break;
case "no-category";
$filter_messages[] = [
'title' => 'Không có danh mục',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `category` = '0' ";
break;
case "display-off";
$filter_messages[] = [
'title' => 'Đang ẩn',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `status`=0 ";
break;
case "display-on";
$filter_messages[] = [
'title' => 'Đang hiển thị',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND `status`=1 ";
break;
case "has-promotion":
$filter_messages[] = [
'title' => 'Có khuyến mại',
'reset' => Url::buildUrl($this->_request_url, ['other_filter' => '']),
];
$where_query[] = " AND LENGTH(`special_offer`) < 5 ";
break;
//...add more
}
}
}
/*// limit by admin permission
//1-1-2014 gioi han theo san pham danh muc ma quan tri nay quan ly
global $admin_permission;
$restricted_category_per_admin = (isset($admin_permission['permit'])) ? $admin_permission['permit']['product:category']['ids'] : [];
if(sizeof($restricted_category_per_admin)) {
$cat_query = $this->db->runQuery("
SELECT DISTINCT pro_id
FROM ".TB_PRODUCT_CATEGORY."
WHERE `category_id` IN (".join(",", $restricted_category_per_admin).")
LIMIT 5000
");
//lay danh sach danh muc cua san pham
$product_in_category = [];
foreach( $this->db->fetchAll($cat_query) as $cat_info ){
$product_in_category[] = $cat_info["pro_id"];
}
$where_query[] = (sizeof($product_in_category)) ? " AND id IN (". join(',', $product_in_category) .") " : " AND id = 0 ";
}*/
//- brand id or ids or brand_indexes
if( array_key_exists("brand", $this->filters) && sizeof($this->filters["brand"])) {
$objBrandModel = new BrandModel();
$condition = array();
foreach ($this->filters["brand"] as $brand_id) {
if(!$brand_id) continue;
$brand_info = $objBrandModel->getInfo( $brand_id);
if(!$brand_info) continue;
$filterPath["brand"][] = array(
"id" => $brand_info['id'],
"name" => $brand_info["title"],
);
$condition[] = " brandId = '".intval($brand_info['id'])."' ";
$filter_messages[] = [
'title' => $brand_info['title'],
'reset' => Url::buildUrl($this->_request_url, ['brand' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($this->filters["brand"], $brand_id ))] ),
];
}
if(sizeof($condition)) {
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
}
//- collection id or ids
if( array_key_exists("collection", $this->filters) && sizeof($this->filters["collection"])) {
$condition = array();
$objProductCollectionModel = new ProductCollectionModel();
foreach ($this->filters["collection"] as $_id) {
$collection_info = $objProductCollectionModel->getInfo($_id);
if(!$collection_info) continue;
$filterPath["collection"][] = array(
"id" => $_id,
"name" => $collection_info["title"],
);
$condition[] = " `id` IN ( SELECT product_id FROM ".TableName::PRODUCT_PER_COLLECTION." WHERE `collection_id`='".intval($collection_info['id'])."' ) ";
$filter_messages[] = [
'title' => 'Bộ sưu tập ',
'reset' => Url::buildUrl($this->_request_url, ['collection' => join(',', remove_item_from_array($this->filters["collection"], $_id))] ),
];
}
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
//- supplier id
if( array_key_exists("supplier", $this->filters) && sizeof($this->filters["supplier"])) {
/*$condition = array();
if(!$this->objSupplier) $this->objSupplier = new Supplier();
foreach ($this->filters["supplier"] as $_id) {
$_id = intval($_id);
$supplier_info = $this->objSupplier->getInfo($_id);
$filterPath["supplier"][] = array(
"id" => $_id,
"name" => $supplier_info["name"],
);
$condition[] = " supplier_id = '".$_id."' ";
$filter_messages[] = [
'title' => $supplier_info["name"],
'reset' => Url::buildUrl($this->_request_url, ['supplier' => join(',', remove_item_from_array($this->filters["supplier"], $_id))] ),
];
}
$where_query[] = " AND ( ".join(" OR ", $condition)." )";*/
}
//- rating: 1-> 5
if( array_key_exists("rating", $this->filters) && sizeof($this->filters["rating"])) {
$condition = array();
foreach ($this->filters["rating"] as $_id) {
$condition[] = " `rating` = '".intval($_id)."' ";
$filterPath["rating"][] = array(
"id" => $_id,
"name" => $_id,
);
$filter_messages[] = [
'title' => 'Đánh giá '.$_id,
'reset' => Url::buildUrl($this->_request_url, ['rating' => join(',', remove_item_from_array($this->filters["rating"], $_id))] ),
];
}
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
//- category id or ids
if( array_key_exists("category", $this->filters) && sizeof($this->filters["category"])) {
$objProductCategoryModel = new ProductCategoryModel();
$condition = array();
foreach ($this->filters["category"] as $cat_id) {
$cat_info = $objProductCategoryModel->getInfo($cat_id);
if(!$cat_info) continue;
if($cat_info["is_parent"]) {
$childListId = ($cat_info["child_ids"]) ?: '0';
$condition[] = " `category_id` IN (".$childListId .") ";
}else{
$condition[] = " `category_id` = '".$cat_id."' ";
}
$filterPath["category"][] = array(
"id" => $cat_id,
"name" => $cat_info['title'],
);
$filter_messages[] = [
'title' => $cat_info['title'],
'reset' => Url::buildUrl($this->_request_url, ['category' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($this->filters["category"], $cat_id))] ),
];
}
if(sizeof($condition)) {
$where_query[] = " AND `id` IN ( SELECT DISTINCT `item_id` FROM ".TableName::PRODUCT_PER_CATEGORY." WHERE " . join(" OR ", $condition) . " )";
}
}
//- status: 0|1|null
if( array_key_exists("status", $this->filters) && sizeof($this->filters["status"])) {
$where_query[] = " AND `status` IN (".join(',', $this->filters["status"]).") ";
}
// spec_group_id
if( array_key_exists("spec_group_id", $this->filters) && sizeof($this->filters["spec_group_id"])) {
$where_query[] = " AND `spec_group_id` IN (".join(',', $this->filters["spec_group_id"]).") ";
}
// detail_page_only: 0 | 1
if( array_key_exists("detail_page_only", $this->filters) && $this->filters["detail_page_only"]) {
$where_query[] = " AND `detail_page_only` = '".intval($this->filters['detail_page_only'])."' ";
}
//- query: search keyword
if( array_key_exists("query", $this->filters) && $this->filters["query"]) {
$keyword_search = $this->filters["query"];
$search_by_product_id = intval(preg_replace('/[^0-9]/i', '', $keyword_search));
// todo: add more
$filter_search = [];
if(isset($this->filters["brand"]) && $this->filters["brand"]) {
$filter_search['brand'] = ["IN", $this->filters["brand"]];
}
if(isset($this->filters["status"]) && $this->filters["status"]) {
$filter_search['status'] = ["IN", $this->filters["status"]];
}
$objProductSearchModel = new ProductSearchModel();
$match_result = $objProductSearchModel->find($keyword_search, $filter_search);
//debug_var($match_result);
$filterPath["search"] = array(
"id" => $keyword_search,
"name" => $keyword_search,
);
$filter_messages[] = [
'title' => $keyword_search,
'reset' => Url::buildUrl($this->_request_url, ['q' => ''] ),
];
if($search_by_product_id > 0) {
$where_query[] = (sizeof($match_result) > 0) ? " AND ( `id` IN (".join(',', $match_result).") OR `id` = '".$search_by_product_id."' ) " : " AND `id` = '".$search_by_product_id."' ";
}else{
$where_query[] = (sizeof($match_result) > 0) ? " AND `id` IN (".join(',', $match_result).") " : " AND `id` = -1 ";
}
}
//- hotType: saleoff | not | new | or combination of types
if( array_key_exists("hotType", $this->filters) && sizeof($this->filters["hotType"])) {
$config_hottype = AProductHotController::getProductHotTypeList();
$condition = array();
foreach ($this->filters["hotType"] as $_id) {
if(!array_key_exists($_id, $config_hottype)) continue;
$condition[] = " `hot_type` = '".$_id."' ";
$filter_messages[] = [
'title' => $config_hottype[$_id],
'reset' => Url::buildUrl($this->_request_url, ['hotType' => '']),
];
}
if(sizeof($condition)) {
$where_query[] = " AND `id` IN (SELECT pro_id FROM ".TB_PRODUCT_HOT." WHERE 1 AND ( ".join(" OR ", $condition)." ) ) ";
}
}
//- attribute values
/*
if( array_key_exists("attribute", $this->filters) && sizeof($this->filters["attribute"])) {
$filter_attr_value_list = $this->filters["attribute"];
//filter = attr_value_1-attr_value_2-attr_value_3,
$query_attr_id = [];
$count_filter = 0;
foreach($filter_attr_value_list as $attr_id){
$attr_id = (int) $attr_id;
if($attr_id) {
$query_attr_id[] = $attr_id;
$count_filter ++;
}
}
$product_filter_id_match = array();
if(sizeof($query_attr_id)) {
$query = $this->db->runQuery("
SELECT DISTINCT pro_id , COUNT(*) as num_pro
FROM ".TB_PRODUCT_ATTRIBUTE."
WHERE attr_value_id IN (".join(',', $query_attr_id).")
GROUP BY pro_id
HAVING num_pro = ".$count_filter."
LIMIT 10000
");
foreach ( $this->db->fetchAll($query) as $rs ) {
$product_filter_id_match[] = $rs["pro_id"];
}
}
$where_query[] = (sizeof($product_filter_id_match)) ? " AND `id` IN (".join(', ', $product_filter_id_match).") " : " AND `id` = 0 " ;
//xay lai url de back
if(!$this->objCategoryProduct) $this->objCategoryProduct = new CategoryProduct();
foreach($filter_attr_value_list as $value_id ){
$att_name = $this->objCategoryProduct->atrValueName($value_id);
$filterPath["attribute"][] = array(
"id" => $value_id,
"name" => $att_name,
);
$filter_messages[] = [
'title' => $att_name,
'reset' => Url::buildUrl($this->_request_url, ['filter' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($this->filters["attribute"], $value_id))] ),
];
}
}
*/
//- attribute values
if( array_key_exists("attribute", $this->filters) && sizeof($this->filters["attribute"])) {
$filter_attr_value_list = $this->filters["attribute"];
$attr_values_per_attribute = $this->groupAttributeValuesByAttributeId($filter_attr_value_list);
$having_match_count = sizeof(array_keys($attr_values_per_attribute));
$attribute_info_list = $this->groupAttributeListInfo(array_keys($attr_values_per_attribute));
$attr_where_query = [];
foreach ($attr_values_per_attribute as $_att_id => $_att_value_list) {
// if same attribute : find products that match either ONE of these values (not match ALL values)
$_att_info = $attribute_info_list[$_att_id];
if (!$_att_info) continue;
// this attribute requires all products must have ALL selected values
if ($_att_info['value_match_all'] && sizeof($_att_value_list) > 1) {
$_product_id_match_all = $this->getProductMatchAllAttributeValueIds($_att_value_list);
if (sizeof($_product_id_match_all) > 0) {
$attr_where_query[] = " ( `id` IN (" . join(",", $_product_id_match_all) . ") ) ";
} else {
$attr_where_query[] = " ( `id` = -1 ) ";
}
} else {
$attr_where_query[] = " ( SELECT DISTINCT `pro_id` FROM " . TB_PRODUCT_ATTRIBUTE . " WHERE (" .
join(" OR ", array_map(function ($_item) { return " `attr_value_id` = '" . intval($_item['id']) . "' "; }, $_att_value_list))
. " ) ) ";
}
}
$product_filter_id_match = array();
if (sizeof($attr_where_query)) {
$query = $this->db->runQuery("
SELECT `pro_id` , COUNT(*) AS num_pro
FROM ( " . join(" UNION ALL ", $attr_where_query) . " ) AS tpm_tb
GROUP BY pro_id
HAVING num_pro = " . $having_match_count . "
LIMIT 10000
");
foreach ($this->db->fetchAll($query) as $rs) {
$product_filter_id_match[] = $rs["pro_id"];
}
}
$where_query[] = (sizeof($product_filter_id_match)) ? " AND " . TB_PRODUCT_LIGHT . ".`id` IN (" . join(', ', $product_filter_id_match) . ") " : " AND " . TB_PRODUCT_LIGHT . ".`id` = 0 ";
//xay lai url de back
$objProductAttributeModel = new ProductAttributeModel();
foreach($filter_attr_value_list as $value_id ){
$att_name = $objProductAttributeModel->getInfo($value_id)['title'];
$filterPath["attribute"][] = array(
"id" => $value_id,
"name" => $att_name,
);
$filter_messages[] = [
'title' => $att_name,
'reset' => Url::buildUrl($this->_request_url, ['filter' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($this->filters["attribute"], $value_id))] ),
];
}
/*//xay lai url de back
$filterPath["attribute"] = $filter_attr_value_list;
foreach ($attr_values_per_attribute as $_att_id => $_att_value_list) {
foreach ($_att_value_list as $_item) {
$filter_messages[] = [
'title' => $_item['title'],
'reset' => Url::buildUrl(CURRENT_URL, ['filter' => join('-', remove_item_from_array($filter_attr_value_list, $_item['id']))]),
];
}
}*/
}
//given products' ids
if( array_key_exists("ids", $this->filters) && sizeof($this->filters["ids"])) {
$filter_messages[] = [
'title' => "IDs",
'reset' => Url::buildUrl($this->_request_url, ['ids' => '']),
];
$where_query[] = " AND `id` IN (". join(",", $this->filters["ids"]) .") ";
}
// exclude some ids
if( array_key_exists("excluded_ids", $this->filters) && sizeof($this->filters["excluded_ids"])) {
$filter_messages[] = [
'title' => "Excluded IDs",
'reset' => Url::buildUrl($this->_request_url, ['excluded_ids' => '']),
];
$where_query[] = " AND `id` NOT IN (". join(",", $this->filters["excluded_ids"]) .") ";
}
$price_range_query_limit = join(" ",$where_query);
//- price range
if(isset($this->filters["price"]) && sizeof($this->filters["price"]) && ($this->filters["price"]['min'] > 0 || $this->filters["price"]['max'] > 0)) {
//limit by price range
$maxPrice = clean_price($this->filters["price"]['max']);
$minPrice = clean_price($this->filters["price"]['min']);
$price_range_query = '';
if($maxPrice > 0 && $minPrice > 0){
$price_range_query = " ( `price` BETWEEN '".$minPrice."' AND '".$maxPrice."' ) ";
}else if($maxPrice > 0){
$price_range_query = " `price` < '".$maxPrice."' ";
}else if($minPrice > 0){
$price_range_query = " `price` >='".$minPrice."' ";
}
/*if(ENABLE_INVENTORY_PER_STORE && USER_LOCATION) {
$list_ids = $this->getProductIds($price_range_query_limit);
$this->objCache->set("all_product_ids_location", $list_ids);
$objUStoreLocation = new UStoreLocation();
$location_products = $objUStoreLocation->getAllProductForLocation(USER_LOCATION, $list_ids, $sort_by);
$location_products = $this->filterLocationProductByPrice($location_products, $maxPrice, $minPrice);
$this->objCache->set("location_products", $location_products);
$location_product_ids = array_keys($location_products);
$where_query[] = (sizeof($location_product_ids)) ? " AND `id` IN (".join(',', $location_product_ids).") " : " AND `id` = 0";
} else {
$where_query[] = " AND ". $price_range_query;
}*/
$where_query[] = " AND ". $price_range_query;
$filterPath["price"] = array(
"min" => $minPrice,
"max" => $maxPrice,
);
$price_format = ProductFilterPrice::buildPriceRangeFormat($minPrice, $maxPrice);
$filter_messages[] = [
'title' => $price_format['title'],
'reset' => Url::buildUrl($this->_request_url, ['p' => '', 'min' => '', 'max' => ''])
];
}
return [
$price_range_query_limit,
$filterPath,
$filter_messages,
$where_query
];
}
protected function getProductMatchAllAttributeValueIds(array $_att_value_list) {
$objProductAttributeModel = new ProductAttributeModel();
return $objProductAttributeModel->getProductMatchAllAttributeValueIds($_att_value_list);
}
protected function groupAttributeListInfo(array $attr_ids) {
$objProductAttributeModel = new ProductAttributeModel();
$attr_list_info = $objProductAttributeModel->getListByIds($attr_ids);
$final_list = [];
foreach ($attr_ids as $_id) {
$final_list[$_id] = (isset($attr_list_info[$_id])) ? $attr_list_info[$_id] : null;
}
return $final_list;
}
protected function groupAttributeValuesByAttributeId(array $attr_value_ids) {
$objProductAttributeValueModel = new ProductAttributeValueModel(0);
$result = array();
foreach ( $objProductAttributeValueModel->getListByIds($attr_value_ids) as $info ) {
$result[$info['attributeId']][] = [
'id' => $info['id'],
'title' => $info['value'],
];
}
return $result;
}
protected function filterLocationProductByPrice(array $product_list, $max_price, $min_price) {
if($max_price > 0 && $min_price > 0){
return array_filter($product_list, function ($pro) use ($max_price, $min_price) {
return ($pro['price'] >= $min_price && $pro['price'] < $max_price);
});
}
if($max_price > 0){
return array_filter($product_list, function ($pro) use ($max_price, $min_price) {
return ($pro['price'] < $max_price);
});
}
if($min_price > 0){
return array_filter($product_list, function ($pro) use ($max_price, $min_price) {
return ($pro['price'] >= $min_price);
});
}
return $product_list;
}
protected function getOrderingClause($sort_by) {
$mappings = [
'order-new' => "ORDER BY `ordering` DESC, `id` DESC",
'order-last-update' => "ORDER BY `ordering` DESC, last_update DESC",
'last-update' => "ORDER BY `last_update` DESC",
'order' => "ORDER BY `ordering` DESC",
'new' => "ORDER BY `id` DESC",
'price-asc' => "ORDER BY `price` asc",
'price-desc' => "ORDER BY `price` DESC ",
'view' => "ORDER BY `visit` desc",
'ranking' => "ORDER BY `ranking` DESC",
'name' => "ORDER BY `title` asc",
];
if(array_key_exists($sort_by, $mappings)) {
return $mappings[$sort_by];
}
return "ORDER BY `ordering` DESC, `id` DESC";
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\Components\Product\Model\ProductFilterModel;
class ProductFilterOptionsController {
protected $_current_category_ids = [];
protected $_list_product_ids = [];
protected $_price_range = [];// [1000000, 5000000, 15000000, 25000000, 50000000];
protected $_filters = [
"price" => array('max' => 0, 'min'=> 0),
"brand" => array(), // array(1,2,3,)
"collection" => array(), // array(1,2,3,)
"supplier" => array(), // array(1,2,3,)
"rating" => array(), // array(1,2,3,)
"category" => array(), // array(1,2,3,)
"status" => array(), // array(1,2,3,)
"query" => "", // string search keyword
"hotType" => array(),// array(saleoff | not | new)
"attribute" => array(), // array(1,2,3,)
"ids" => array(), // array(1,2,3,)
"promotion" => "",
"storeId" => array(), // array(1,2,3,)
"other_filter" => array() // array(in-stock, has-promotion etc...)
];
protected $objProductFilterModel;
public function __construct($filters, $current_category_ids = [], $list_product_ids = [], $price_range = []) {
$this->objProductFilterModel = new ProductFilterModel();
$this->_filters = $filters;
$this->_current_category_ids = $current_category_ids;
$this->_list_product_ids = $list_product_ids;
$this->_price_range = $price_range;
}
public function ratingList() {
$neededList = array();
foreach ( $this->objProductFilterModel->ratingList($this->_list_product_ids) as $result ) {
$neededList[] = array(
"rating" => $result['review_rate'] ,
"count" => $result['num'] ,
'is_selected' => in_array($result['review_rate'], $this->_filters['rating']),
);
}
return $neededList;
}
public function categoryList() {
if(!sizeof($this->_list_product_ids)) {
return [];
}
return $this->objProductFilterModel->categoryList($this->_filters['category'], $this->_list_product_ids);
}
public function brandList() {
if(!sizeof($this->_list_product_ids)) {
return [];
}
return $this->objProductFilterModel->brandList($this->_filters['brand'], $this->_list_product_ids);
}
public function attributeList() {
return $this->objProductFilterModel->attributeList(
$this->_filters['attribute'],
$this->_current_category_ids,
$this->_list_product_ids
);
}
public function priceList() {
return $this->objProductFilterModel->priceList(
$this->_filters['price']['max'] ,
$this->_filters['price']['min'],
$this->_list_product_ids,
$this->_price_range
);
}
}

View File

@@ -0,0 +1,348 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\System\Url;
class ProductFilterOptionsTranslationController {
protected $_request_url = '';
protected $_url_elements = [
'scheme' => '',
'host' => '',
'port' => '',
'user' => '',
'pass' => '',
'path' => '',
'query' => [],
'fragment' => '',
];
protected $_price_format = '';
protected $_brand_format = '';
protected $_filter_format = '';
public function __construct($request_url) {
if(defined('PRICE_FILTER_FORMAT') && PRICE_FILTER_FORMAT) {
$this->_price_format = PRICE_FILTER_FORMAT;
}
if(defined('BRAND_FILTER_FORMAT') && BRAND_FILTER_FORMAT) {
$this->_brand_format = BRAND_FILTER_FORMAT;
}
if(defined('ATTRIBUTE_FILTER_FORMAT') && ATTRIBUTE_FILTER_FORMAT ) {
$this->_filter_format = ATTRIBUTE_FILTER_FORMAT;
}
$this->_request_url = $request_url;
$this->_url_elements = Url::parse($request_url);
}
public function getAllFilterOptions(array $category_list = [], array $attribute_list = [], array $brand_list = [], array $price_list_options = []) {
$result = [
'category' => $category_list,
'price' => $this->translatePriceList($price_list_options),
'brand' => $this->translateBrandList($brand_list),
'attribute' => $this->translateAttributeList($attribute_list),
];
$selected_categories = array_filter($category_list, function ($item){ return $item['is_selected'];});
$selected_prices = array_filter($result['price'], function ($item){ return $item['is_selected'];});
$selected_brands = array_filter($result['brand'], function ($item){ return $item['is_selected'];});
$selected_attributes = array_filter($result['attribute'], function ($item){ return $item['is_selected'];});
$filter_messages = [];
foreach ($selected_categories as $selected_category) {
$filter_messages[] = [
'name' => $selected_category['name'], // 'Danh mục: '.
'reset_url' => Url::buildUrl($this->_request_url, ['category' => ''] ),
];
}
foreach ($selected_prices as $selected_price) {
$filter_messages[] = [
'name' => $selected_price['name'], // 'Giá: '.
'reset_url' => $selected_price['url'],
];
}
foreach ($selected_brands as $selected_brand) {
$filter_messages[] = [
'name' => $selected_brand['name'], // 'Thương hiệu: '.
'reset_url' => $selected_brand['url'],
];
}
foreach ($selected_attributes as $selected_attribute) {
$selected_attribute_values = array_filter($selected_attribute['value_list'], function ($item){ return $item['is_selected'];});
foreach ($selected_attribute_values as $selected_attribute_value) {
$filter_messages[] = [
'name' => $selected_attribute_value['name'], //$selected_attribute['name'].': '.$selected_attribute_value['name'],
'reset_url' => $selected_attribute_value['url'],
];
}
}
return [$result, $filter_messages];
}
public function translateAttributeList(array $attribute_list = []) {
$result = [];
foreach ($attribute_list as $attr_info) {
$copy = $attr_info;
$copy['value_list'] = array_map(function ($value_info) use ($attr_info) {
return $this->translateAttributeValue($attr_info['filter_code'], $value_info);
}, $attr_info['value_list']);
$result[] = $copy;
}
return $result;
}
protected function translateAttributeValue($attribute_filter_code, array $value_info = []) {
$copy = $value_info;
if($copy['is_selected']) {
$copy['url'] = Url::buildUrl($this->_request_url, $this->buildAttributeValueUrlResetParameters($attribute_filter_code, $value_info['id'], $value_info['api_key']) );
}else{
$copy['url'] = Url::buildUrl($this->_request_url, $this->buildAttributeValueUrlParameters($attribute_filter_code, $value_info['id'], $value_info['api_key']) );
}
//$copy['reset_url'] = Url::buildUrl($this->_request_url, $this->buildAttributeValueUrlResetParameters($attribute_filter_code, $value_info['id'], $value_info['api_key']) );
return $copy;
}
protected function buildAttributeValueUrlParameters($attribute_filter_code, $value_id, $value_api_key) {
if($this->_filter_format == 'filter_code') {
$filter_query = (isset($this->_url_elements['query'][$attribute_filter_code])) ? $this->_url_elements['query'][$attribute_filter_code] : '';
$filter_value_list = ($filter_query) ? explode(FILTER_VALUE_SEPARATOR, $filter_query) : [];
$filter_value_list[] = $value_api_key;
$result = [];
$result[$attribute_filter_code] = join(FILTER_VALUE_SEPARATOR, $filter_value_list );
return $result;
}
// default
$filter_query = (isset($this->_url_elements['query']['filter'])) ? $this->_url_elements['query']['filter'] : '';
$filter_value_list = ($filter_query) ? explode(FILTER_VALUE_SEPARATOR, $filter_query) : [];
$filter_value_list[] = $value_id;
sort($filter_value_list);
return [
'filter' => join(FILTER_VALUE_SEPARATOR, $filter_value_list ),
];
}
protected function buildAttributeValueUrlResetParameters($attribute_filter_code, $value_id, $value_api_key) {
if($this->_filter_format == 'filter_code') {
$filter_query = (isset($this->_url_elements['query'][$attribute_filter_code])) ? $this->_url_elements['query'][$attribute_filter_code] : '';
$filter_value_list = ($filter_query) ? explode(FILTER_VALUE_SEPARATOR, $filter_query) : [];
$filter_value_list = remove_item_from_array($filter_value_list, $value_api_key);
$result = [];
$result[$attribute_filter_code] = join(FILTER_VALUE_SEPARATOR, $filter_value_list );
return $result;
}
// default
$filter_query = (isset($this->_url_elements['query']['filter'])) ? $this->_url_elements['query']['filter'] : '';
$filter_value_list = ($filter_query) ? explode(FILTER_VALUE_SEPARATOR, $filter_query) : [];
$filter_value_list = remove_item_from_array($filter_value_list, $value_id);
return [
'filter' => join(FILTER_VALUE_SEPARATOR, $filter_value_list ),
];
}
public function translateBrandList(array $brand_list = []) {
return array_map(function ($item){
$copy = $item;
if(($copy['is_selected'])) {
$copy['url'] = Url::buildUrl($this->_request_url, $this->buildBrandUrlResetParameters($item['id'], $item['brand_index']) ) ;
}else{
$copy['url'] = Url::buildUrl($this->_request_url, $this->buildBrandUrlParameters($item['id'], $item['brand_index']) );
}
//$copy['reset_url'] = Url::buildUrl($this->_request_url, $this->buildBrandUrlResetParameters($item['id'], $item['brand_index']) );
//unset($copy['brand_index']);
return $copy;
}, $brand_list);
}
protected function buildBrandUrlResetParameters($brand_id, $brand_index){
$brand_query = (isset($this->_url_elements['query']['brand'])) ? $this->_url_elements['query']['brand'] : '';
$brand_list = ($brand_query) ? explode(FILTER_VALUE_SEPARATOR, $brand_query) : [];
if($this->_brand_format == 'brand_index') {
$brand_list = remove_item_from_array($brand_list, $brand_index);
}else{
$brand_list = remove_item_from_array($brand_list, $brand_id);
}
return [
'brand' => join(FILTER_VALUE_SEPARATOR, $brand_list ),
];
}
protected function buildBrandUrlParameters($brand_id, $brand_index){
$brand_query = (isset($this->_url_elements['query']['brand'])) ? $this->_url_elements['query']['brand'] : '';
$brand_list = ($brand_query) ? explode(FILTER_VALUE_SEPARATOR, $brand_query) : [];
if($this->_brand_format == 'brand_index') {
if(!in_array($brand_index, $brand_list)) $brand_list[] = $brand_index;
}else{
if(!in_array($brand_id, $brand_list)) $brand_list[] = $brand_id;
}
return [
'brand' => join(FILTER_VALUE_SEPARATOR, $brand_list ),
];
}
public function translatePriceList(array $price_list_options = []) {
return array_map(function ($item){
$copy = $item;
$format = $this->buildPriceRangeFormat($item['min'], $item['max']);
$copy['name'] = $format['name'];
$copy['url'] = ($copy['is_selected']) ? Url::buildUrl($this->_request_url, $this->buildPriceUrlResetParameters()) : Url::buildUrl($this->_request_url, $format['url_para']);
//$copy['reset_url'] = Url::buildUrl($this->_request_url, $this->buildPriceUrlResetParameters());
//unset($copy['min'], $copy['max']);
return $copy;
}, $price_list_options);
}
protected function buildPriceRangeFormat($min_price, $max_price) {
if($min_price > 0 && !$max_price) {
$title = "Trên ".$this->format_search_price($min_price);
}elseif(!$min_price && $max_price > 0) {
$title = "Dưới ".$this->format_search_price($max_price);
}else{
$title = $this->format_search_price($min_price)." - ".$this->format_search_price($max_price);
}
return [
'url_para' => $this->buildPriceUrlParameters($min_price, $max_price),
'name' => $title,
];
}
protected function format_search_price($p_price){
$price_len = strlen($p_price);
if($price_len < 7) {
return round($p_price/1000,1)." ngàn";
}
else if($price_len < 10) {
return round($p_price/1000000,1)." triệu";
}
return round($p_price/1000000000,1)." tỷ";
}
protected function buildPriceUrlResetParameters() {
if( $this->_price_format == 'p') {
return [
"p" => '',
];
}
return [
"min" => '' ,
"max" => '' ,
];
}
protected function buildPriceUrlParameters($min_price = 0, $max_price = 0) {
if($min_price > 0 && !$max_price) {
if( $this->_price_format == 'p') {
return [
"p" => "tren-".self::convertPriceFormatToFilter($this->format_search_price($min_price)),
];
}
return [
"min" => $min_price ,
];
}
if(!$min_price && $max_price > 0) {
if( $this->_price_format == 'p') {
return [
"p" => "duoi-" . self::convertPriceFormatToFilter($this->format_search_price($max_price)),
];
}
return [
"max" => $max_price,
];
}
if( $this->_price_format == 'p') {
return [
"p" => self::convertPriceFormatToFilter($this->format_search_price($min_price)) . "-" . self::convertPriceFormatToFilter($this->format_search_price($max_price)),
];
}
return [
"max" => $max_price ,
"min" => $min_price,
];
}
// convert: 10 ngàn -> 10ngan, 10 triệu => 10trieu
protected static function convertPriceFormatToFilter($price_format){
$price_format = str_replace(['ngàn', 'triệu'], ['ngan', 'trieu'], $price_format);
// remove whitespace
return str_replace(" ",'', $price_format);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\Components\Product\Model\ProductCategoryLanguageModel;
use Hura8\Components\Product\Model\ProductCategoryModel;
use Hura8\System\Controller\aCategoryBaseController;
class bProductCategoryController extends aCategoryBaseController
{
static $image_folder = "media/category";
protected $objProductCategoryModel;
public function __construct()
{
$this->objProductCategoryModel = new ProductCategoryModel();
if(!$this->isDefaultLanguage()) {
//$this->objProductCategoryLanguageModel->createTableLang();
parent::__construct(
$this->objProductCategoryModel,
new ProductCategoryLanguageModel()
);
}else{
parent::__construct(
$this->objProductCategoryModel
);
}
}
protected function formatItemInfo(?array $info) : ?array
{
if(!$info) return null;
if($info['icon']) $info['icon'] = STATIC_DOMAIN . "/" . static::$image_folder . "/" . $info['icon'];
return $info;
}
// get full info- basic with description
public function getFullInfo($id): ?array
{
$info = $this->objProductCategoryModel->getFullInfo($id);
if(!$info) return null;
if($this->iEntityLanguageModel) {
$item_language_info = $this->iEntityLanguageModel->getInfo($id);
if($item_language_info) {
return $this->formatItemInfo(array_merge($info, $item_language_info));
}
}
return $this->formatItemInfo($info);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\Components\Product\Model\ProductCollectionLanguageModel;
use Hura8\Components\Product\Model\ProductCollectionModel;
use Hura8\System\Controller\aCategoryBaseController;
class bProductCollectionController extends aCategoryBaseController
{
static $image_folder = "media/category";
protected $objProductCollectionModel;
public function __construct()
{
$this->objProductCollectionModel = new ProductCollectionModel();
if(!$this->isDefaultLanguage()) {
//$this->objProductCategoryLanguageModel->createTableLang();
parent::__construct(
$this->objProductCollectionModel,
new ProductCollectionLanguageModel()
);
}else{
parent::__construct(
$this->objProductCollectionModel
);
}
}
public function getTotalProduct($collection_id, array $condition = [])
{
return $this->objProductCollectionModel->getTotalProduct($collection_id, $condition);
}
public function getListProduct($collection_id, array $condition = []) {
return $this->objProductCollectionModel->getListProduct($collection_id, $condition);
}
protected function formatItemInList(array $info) : array
{
return $this->formatItemInfo($info);
}
protected function formatItemInfo(?array $info) : ?array
{
$info['url'] = "/collection/".$info['url_index'];
return $info;
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace Hura8\Components\Product\Controller;
use Hura8\Components\Product\Model\ProductImageModel;
use Hura8\Components\Product\Model\ProductLanguageModel;
use Hura8\Components\Product\Model\ProductModel;
use Hura8\System\Controller\aEntityBaseController;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
use Hura8\System\Url;
abstract class bProductController extends aEntityBaseController
{
static $image_folder = "media/product";
static $resized_sizes = array(
't' => ['width' => 100,] ,
's' => ['width' => 300,] ,
'l' => ['width' => 650,]
);
protected $objProductModel;
public function __construct()
{
$this->objProductModel = new ProductModel();
if(!$this->isDefaultLanguage()) {
$objProductLanguageModel = new ProductLanguageModel();
// $this->objProductLanguageModel->createTableLang();
parent::__construct($this->objProductModel, $objProductLanguageModel);
}else{
parent::__construct($this->objProductModel);
}
}
//get product image list
public function productImageList($proId){
$objProductImageModel = new ProductImageModel($proId);
$result = array();
foreach ( $objProductImageModel->getList(["numPerPage" => 100]) as $rs ) {
$result[] = [
"size" => static::getResizedImageCollection($rs['img_name']),
"alt" => $rs['alt'],
"folder" => $rs['folder'],
];
}
return $result;
}
public function getProductDisplayOptions($current_url) {
$allowed_options = array(
"list" => "Danh sách",
"grid" => "Xem nhóm",
"detail" => "Chi tiết"
) ;
$collection = array();
foreach($allowed_options as $option => $name){
$url = Url::buildUrl($current_url, array("display" => ""));
$collection[] = array(
"url" => Url::buildUrl($url, array("display" => $option)) ,
"key" => $option ,
"name" => $name ,
);
}
return $collection;
}
public function getProductOtherFilterOptions($current_url) {
$allowed_options = array(
//"order" => $global_language['sort_by_order'] ,
"in-stock" => 'Còn hàng' ,
) ;
$_collection = array();
foreach($allowed_options as $option => $_name){
$_collection[] = array(
"url" => Url::buildUrl($current_url, array("other_filter" => $option, 'page' => '')) ,
"key" => $option ,
"name" => $_name ,
);
}
return $_collection;
}
public function getProductSortOptions($current_url, array $options = []) {
//global $global_language;
if(!sizeof($options)) $options = array(
//"order" => $global_language['sort_by_order'] ,
"new" => "Mới nhất" ,
"price-asc" => "Giá tăng dần", //$global_language['sort_by_price_asc'] ,
"price-desc" => "Giá giảm dần", //$global_language['sort_by_price_desc'] ,
"view" => "Lượt xem", //$global_language['sort_by_view'],
"comment" => "Trao đổi", //$global_language['sort_by_comment'] ,
"rating" => "Đánh giá", //$global_language['sort_by_rating'] ,
"name" => "Tên A->Z" ,
) ;
$sort_by_collection = array();
foreach($options as $sort_option=>$sort_name){
// $url = build_url($current_url, array("sort" => ""));
$sort_by_collection[] = array(
"url" => Url::buildUrl($current_url, array("sort" => $sort_option)) ,
"key" => $sort_option ,
"name" => $sort_name ,
);
}
return $sort_by_collection;
}
public function getProductInfoBySKU($sku) {
return self::getCache(md5("getFullInfo-".$sku), function () use ($sku) {
$info = $this->objProductModel->getProductInfoBySKU($sku);
return ($info) ? $this->formatItemInfo($info) : null;
});
}
// get full info- basic with description
public function getFullInfo($id)
{
return self::getCache("getFullInfo-".$id, function () use ($id) {
$info = $this->objProductModel->getFullInfo($id);
if($this->iEntityLanguageModel && $info) {
$item_language_info = $this->iEntityLanguageModel->getInfo($id);
if($item_language_info) {
return $this->formatItemInfo(array_merge($info, $item_language_info));
}
}
return ($info) ? $this->formatItemInfo($info) : null;
});
}
protected function validateAndCleanFilterCondition(array $raw_filter_condition): array
{
$clean_values = parent::validateAndCleanFilterCondition($raw_filter_condition);
// special cases for 'price' which is in range
if(array_key_exists('price', $raw_filter_condition)) {
$value = $raw_filter_condition['price'];
// expect $value = array('max' => 0, 'min'=> 0)
if(isset($value['max']) && isset($value['min'])) {
$clean_values['price'] = array(
'max' => DataClean::makeInputSafe($value['max'], DataType::INTEGER),
'min' => DataClean::makeInputSafe($value['min'], DataType::INTEGER),
);
}
}
return $clean_values;
}
protected function formatItemInList(array $item_info) : array
{
return $this->formatItemInfo($item_info);
}
protected function formatItemInfo(?array $item_info): ?array
{
if(!$item_info) return null;
$info = $item_info;
$info['image'] = static::getResizedImageCollection($info['thumbnail']);
return $info;
}
public static function getResizedImageCollection($image_name) {
$image = [];
$size_in_full = [
't' => 'thumb' ,
's' => 'small' ,
'l' => 'large' ,
];
foreach (static::$resized_sizes as $size => $value) {
$image[$size_in_full[$size]] = ($image_name) ? STATIC_DOMAIN . "/". static::$image_folder . "/". $size. IMAGE_FILE_SEPARATOR . $image_name : '';
}
$image['original'] = ($image_name) ? STATIC_DOMAIN . "/". static::$image_folder . "/". $image_name : '';
return $image;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\System\Model\EntityLanguageModel;
use Hura8\Interfaces\EntityType;
class ProductAttributeLanguageModel extends EntityLanguageModel
{
public function __construct() {
parent::__construct(EntityType::PRODUCT_ATTRIBUTE);
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Language;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
class ProductAttributeModel extends aEntityBaseModel {
protected $tb_product_category = "tb_product_category";
protected $tb_attribute_value = "tb_attribute_value";
protected $tb_product_spec_group = "tb_product_spec_group";
protected $tb_attribute_per_spec_group = "tb_attribute_per_spec_group";
protected $tb_attribute_per_category = "tb_attribute_per_category";
public function __construct() {
parent::__construct(EntityType::PRODUCT_ATTRIBUTE);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
// SPEC-GROUP
public function getSpecGroupAttributeWithValues($group_id) {
$query = $this->db->runQuery(
"SELECT
a.title,
a.summary,
a.is_filter,
a.value_match_all,
a.filter_code,
a.is_display,
a.is_header,
a.is_multi, a.in_summary,
a.value_count,
g.attr_id,
g.ordering,
g.status
FROM ".$this->tb_attribute_per_spec_group." g, ".$this->tb_entity." a
WHERE g.`group_id`= ? AND g.`attr_id`= a.`id`
ORDER BY g.`ordering` DESC, a.`ordering` DESC ",
['d'],
[$group_id]
) ;
$attribute_ids = [];
$value_per_attribute_ids = [];
$result = [];
foreach ($this->db->fetchAll($query) as $item) {
$attribute_ids[] = $item['attr_id'];
$value_per_attribute_ids[$item['attr_id']] = [];
$result[$item['attr_id']] = [
"attribute_info" => $item,
"attribute_values" => &$value_per_attribute_ids[$item['attr_id']],
];
}
if(!sizeof($attribute_ids)) {
return [];
}
// now get all values for each attribute
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($attribute_ids, "int");
$query = $this->db->runQuery(
"SELECT * FROM ".$this->tb_attribute_value."
WHERE `attribute_id` IN (".$parameterized_ids.")
ORDER BY `ordering` DESC, `title` ASC ",
$bind_types,
$attribute_ids
) ;
foreach ($this->db->fetchAll($query) as $item) {
$value_per_attribute_ids[$item['attribute_id']][] = $item;
}
return $result;
}
public function getSpecGroupAttribute($group_id) {
$query = $this->db->runQuery(
"SELECT
a.title,
a.summary,
a.is_filter,
a.value_match_all,
a.filter_code,
a.is_display,
a.is_header, a.is_multi, a.value_count,
g.attr_id,
g.ordering,
g.status
FROM ".$this->tb_attribute_per_spec_group." g, ".$this->tb_entity." a
WHERE g.`group_id`= ? AND g.`attr_id`= a.`id`
ORDER BY g.`ordering` DESC, a.`ordering` DESC ",
['d'],
[$group_id]
) ;
return $this->db->fetchAll($query);
}
public function getProductMatchAllAttributeValueIds(array $_att_value_list) {
/*$query = $this->db->runQuery("
SELECT `pro_id` , COUNT(*) AS num_pro
FROM ".TB_PRODUCT_ATTRIBUTE."
WHERE " . join(" OR ", array_map(function ($_item){ return " `attr_value_id` = '".intval($_item['id'])."' "; }, $_att_value_list )) ."
GROUP BY pro_id
HAVING num_pro = ".sizeof($_att_value_list)."
LIMIT 10000
");
return array_map(function ($item){
return $item["pro_id"];
}, $this->db->fetchAll($query));*/
return [];
}
public function getProductMatchAttributeValue(array $value_ids) {
$product_filter_id_match = array();
// todo:
/*if(sizeof($query_attr_id)) {
$query = $this->db->runQuery("
SELECT DISTINCT pro_id , COUNT(*) as num_pro
FROM ".TB_PRODUCT_ATTRIBUTE."
WHERE attr_value_id IN (".join(',', $query_attr_id).")
GROUP BY pro_id
HAVING num_pro = ".$count_filter."
LIMIT 10000
");
foreach ( $this->db->fetchAll($query) as $rs ) {
$product_filter_id_match[] = $rs["pro_id"];
}
}*/
return $product_filter_id_match;
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"q" => "",
"letter" => "",
"status" => 0,
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
class ProductAttributeValueModel extends aEntityBaseModel
{
const MAX_VALUE_PER_ATTRIBUTE = 100; // one attribute should not have more that this number of values
protected $attribute_id = 0;
protected $tb_attribute = 'tb_attribute';
protected $tb_product_attribute = 'tb_product_attribute';
public function __construct($attribute_id) {
parent::__construct(EntityType::PRODUCT_ATTRIBUTE_VALUE);
$this->attribute_id = $attribute_id;
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getProductAttributes($product_id) {
$query = $this->db->runQuery(
"SELECT `attr_id`, `attr_value_id` FROM `".$this->tb_product_attribute."` WHERE `pro_id` = ? ",
['d'], [ $product_id]
);
return $this->db->fetchAll($query);
}
protected function getAttributeValueCount() {
$query = $this->db->runQuery(
"SELECT `value_count` FROM `".$this->tb_attribute."` WHERE `id` = ? LIMIT 1 ",
['d'], [$this->attribute_id]
);
if($info = $this->db->fetchAssoc($query)) {
return $info['value_count'];
}
return 0;
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$catCondition = [" AND `attribute_id` = ? "];
$bind_types = ["d"];
$bind_values = [$this->attribute_id];
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
public function getInfoByCode($filter_code)
{
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` WHERE `attribute_id` = ? AND `filter_code` = ? LIMIT 1",
['d', 's'], [$this->attribute_id, $filter_code ]
);
return $this->db->fetchAssoc($query);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Database\iConnectDB;
class ProductCategoryInfoModel
{
/* @var $db iConnectDB */
protected $db;
protected $tb_category_info = "tb_product_category_info";
public function __construct() {
$this->db = get_db();
}
public function getInfo($id) {
return $this->db->select(
$this->tb_category_info,
[],
[
'id' => ['=', $id],
]
);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\System\Model\EntityLanguageModel;
use Hura8\Interfaces\EntityType;
class ProductCategoryLanguageModel extends EntityLanguageModel
{
protected $richtext_fields = [
'description',
];
public function __construct() {
parent::__construct(EntityType::PRODUCT_CATEGORY, '', $this->richtext_fields);
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Controller\UrlManagerController;
use Hura8\System\Model\aCategoryBaseModel;
use Hura8\System\ModuleManager;
use Hura8\Interfaces\EntityType;
class ProductCategoryModel extends aCategoryBaseModel
{
static $url_module = "product";
static $url_view = "category";
static $url_type = "product:category";
protected $tb_category_info = "tb_product_category_info";
protected $tb_product_per_category = "tb_product_per_category";
protected $tb_attribute_per_category = "tb_attribute_per_category";
protected $tb_attribute_value = "tb_attribute_value";
protected $tb_attribute = "tb_attribute";
public function __construct() {
parent::__construct(EntityType::PRODUCT_CATEGORY);
}
public function getAttributeList($catId) {
$query = $this->db->runQuery("SELECT
a.id ,
a.filter_code ,
a.title ,
a.value_count ,
a.is_filter ,
c.ordering ,
c.status ,
a.is_header
FROM ".$this->tb_attribute." a
LEFT JOIN ".$this->tb_attribute_per_category." c ON a.id = c.attr_id
WHERE c.category_id = ?
ORDER BY c.ordering desc
", ['d'], [ $catId ]) ; // AND isSearch = 1
return $this->db->fetchAll($query);
}
public function getFullInfo($id) : ?array {
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` basic, `".$this->tb_category_info."` info
WHERE basic.`id` = info.`id` AND basic.id = ?
LIMIT 1 ",
['d'], [$id]
);
if( $item_info = $this->db->fetchAssoc($query)){
$item_info['settings'] = ($item_info['settings']) ? \json_decode($item_info['settings'], true) : [];
return $item_info;
}
return null;
}
public function getEmptyInfo($addition_field_value = []) : array
{
// basic table
$basic_empty = parent::getEmptyInfo($addition_field_value);
// info table
foreach ($this->db->getTableInfo($this->tb_category_info) as $field => $field_info) {
$basic_empty[$field] = ( in_array($field_info['DATA_TYPE'], ['int', 'float']) ) ? 0 : '' ;
}
if(sizeof($addition_field_value)) {
return array_merge($basic_empty, $addition_field_value);
}
return $basic_empty;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\EntityType;
use Hura8\System\Model\EntityLanguageModel;
class ProductCollectionLanguageModel extends EntityLanguageModel
{
protected $richtext_fields = [
'description',
];
public function __construct() {
parent::__construct(EntityType::PRODUCT_COLLECTION, '', $this->richtext_fields);
//$this->createTableLang();
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\EntityType;
use Hura8\System\Model\aCategoryBaseModel;
class ProductCollectionModel extends aCategoryBaseModel
{
protected $tb_collection_product = "tb_collection_product";
public function __construct() {
parent::__construct(EntityType::PRODUCT_COLLECTION);
}
public function getInfoByUrl($url): ?array
{
$query = $this->db->runQuery(
" SELECT * FROM `".$this->tb_entity."` WHERE `url_index` = ? LIMIT 1",
['s'], [$url]
);
return $this->db->fetchAssoc($query);
}
public function getTotalProduct($collection_id, array $condition = [])
{
$query = $this->db->runQuery(
" SELECT COUNT(*) as total FROM `".$this->tb_collection_product."` WHERE `collection_id` = ? ",
['d'], [$collection_id]
);
$total = 0;
if ($rs = $this->db->fetchAssoc($query)) {
$total = $rs['total'];
}
return $total;
}
public function getListProduct($collection_id, array $condition = [])
{
$numPerPage = (isset($condition['numPerPage']) && $condition['numPerPage'] > 0 ) ? intval($condition['numPerPage']) : 20 ;
$page = (isset($condition['page']) && $condition['page'] > 0 ) ? intval($condition['page']) : 1 ;
$order_by = " `ordering` DESC, `id` DESC";
$query = $this->db->runQuery(
"SELECT `product_id`, `ordering` FROM ".$this->tb_collection_product." WHERE `collection_id` = ?
ORDER BY ".$order_by."
LIMIT ".(($page-1) * $numPerPage).", ".$numPerPage ,
['d'], [$collection_id]
) ;
$item_list = array();
$counter = ($page-1) * $numPerPage;
foreach ( $this->db->fetchAll($query) as $item ) {
$counter += 1;
$item_list[$item['product_id']] = [
'counter' => $counter,
'ordering' => $item['ordering'],
];
}
$objProductModel = new ProductModel();
$product_list_info = $objProductModel->getListByIds(array_keys($item_list));
// final list
$final_list = [];
foreach ($item_list as $_pro_id => $_pro_info_in_collection) {
$pro_basic = $product_list_info[$_pro_id] ?? null;
if($pro_basic) {
$final_list[] = array_merge($pro_basic, $_pro_info_in_collection);
}
}
return $final_list;
}
}

View File

@@ -0,0 +1,452 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Components\Brand\Model\BrandModel;
use Hura8\Database\iConnectDB;
use Hura8\Interfaces\TableName;
use Hura8\Traits\ClassCacheTrait;
class ProductFilterModel
{
use ClassCacheTrait;
/* @var $db iConnectDB */
protected $db;
public function __construct() {
$this->db = get_db('', ENABLE_DB_DEBUG);
}
public function findProductListIdMatchFilters(array $SQLConditionFromFilters) {
list(
$where_query,
$current_page_id,
$number_per_page,
$ordering_clause
) = $SQLConditionFromFilters;
$db_condition = join(" ", $where_query);
$total_number = 0;
$query = $this->db->runQuery("SELECT COUNT(`id`) AS total FROM ".TableName::PRODUCT." WHERE 1 ".$db_condition." ");
if($rs = $this->db->fetchAssoc($query)) {
$total_number = $rs['total'];
}
$query = $this->db->runQuery("
SELECT * FROM ".TableName::PRODUCT."
WHERE 1 ".$db_condition."
".$ordering_clause."
LIMIT ". ($current_page_id - 1) * $number_per_page .", ".$number_per_page."
");
$item_list = $this->db->fetchAll($query);
return [$total_number, $item_list];
}
public function ratingList(array $list_product_ids) {
if(!sizeof($list_product_ids)) return [];
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($list_product_ids, 'int');
$query = $this->db->runQuery(
"
SELECT `review_rate`, COUNT(`id`) as num
FROM ".TableName::PRODUCT."
WHERE `id` IN (".$parameterized_ids.")
GROUP BY `review_rate`
ORDER BY review_rate ASC
",
$bind_types,
$list_product_ids
);
return $this->db->fetchAll($query);
}
public function categoryList(array $current_category_ids, array $list_product_ids) {
if(!sizeof($list_product_ids)) {
return [];
}
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($list_product_ids, 'int');
$query = $this->db->runQuery(
"
SELECT `category_id`, COUNT(`item_id`) AS numPro
FROM ".TableName::PRODUCT_PER_CATEGORY."
WHERE `item_id` IN (".$parameterized_ids.")
GROUP BY `category_id` ",
$bind_types,
$list_product_ids
);
//lay ket qua dem
$result_count = [];
foreach ($this->db->fetchAll($query) as $rs ){
$result_count[$rs['category_id']] = $rs['numPro'];
}
if(sizeof($result_count)) {
$objProductCategoryModel = new ProductCategoryModel();
$final_result = [];
foreach ($objProductCategoryModel->getListByIds(array_keys($result_count)) as $rs ){
$rs['count'] = $result_count[$rs['id']];
$rs['is_selected'] = in_array($rs['id'], $current_category_ids);
$final_result[] = $rs;
}
return $final_result;
}
return [];
}
public function brandList(array $current_brand_ids, array $list_product_ids) {
if(!sizeof($list_product_ids)) {
return [];
}
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($list_product_ids, 'int');
$query = $this->db->runQuery(
" SELECT `brand_id`, COUNT(`id`) AS total
FROM ".TableName::PRODUCT."
WHERE `id` IN (".$parameterized_ids.")
GROUP BY `brand_id`
ORDER BY total DESC
LIMIT 100",
$bind_types,
$list_product_ids
);
$neededList = array();
foreach ( $this->db->fetchAll($query) as $result ) {
$neededList[$result['brand_id']] = array(
"id" => $result['brand_id'] ,
"count" => $result['total'] ,
);
}
$brand_list_ids[] = array_keys($neededList);
$result_list = array();
if(sizeof($brand_list_ids)) {
$objBrandModel = new BrandModel();
foreach ( $objBrandModel->getListByIds($brand_list_ids) as $_brand ) {
$item_count = (isset($neededList[$_brand['id']])) ? $neededList[$_brand['id']]['count'] : 0;
$_brand['count'] = $item_count;
$_brand["is_selected"] = in_array($_brand['id'], $current_brand_ids);
$result_list[$_brand['id']] = $_brand;
}
}
return array_values($result_list);
}
public function attributeList(array $current_filter_attribute_value_ids, array $current_category_ids, array $list_product_ids) {
if(!sizeof($list_product_ids)) {
return [];
}
$category_attribute_filter_array = $this->countNumProPerAttributeForCategory($current_category_ids, $list_product_ids);
if(!sizeof($category_attribute_filter_array)) {
return [];
}
$filter_box = array();
foreach($category_attribute_filter_array as $attributeIdGroup){
$attribute_value_array = $attributeIdGroup['value_list'];
$attribute_name = $attributeIdGroup['name'];
$attribute_selected = 0;
$filterList = array();
$count_num = 0;
foreach($attribute_value_array as $att_value_id => $rs_value){
$att_value_name = $rs_value["info"]["name"];
$att_value_description = $rs_value["info"]["description"];
$count_pro = $rs_value["pro_count"];
$is_selected = in_array($att_value_id, $current_filter_attribute_value_ids);
if($is_selected) {
$attribute_selected = 1;
}
unset($this_filter_query, $this_filter_col_value);
//gioi han danh sach
if($count_num > 50) {
break;
}
$count_num++;
$filterList[] = array(
"id" => $att_value_id ,
"name" => $att_value_name ,
'api_key' => $rs_value["info"]["api_key"],
"description" => $att_value_description,
"count" => $count_pro ,
"is_selected" => $is_selected,
);
}
if($count_num > 0){
$filter_box[] = [
"name" => $attribute_name,
'filter_code' => $attributeIdGroup['filter_code'],
"is_selected" => $attribute_selected,
"value_list" => $filterList ,
];
}
}
return $filter_box;
}
protected function countNumProPerAttributeForCategory(array $current_category_ids, array $list_product_ids){
if(!sizeof($list_product_ids)) {
return [];
}
$category_attributes = $this->getAttributesForCategory($current_category_ids);
$att_value_id_list = [];
foreach ($category_attributes as $attribute) {
$att_value_id_list = array_merge($att_value_id_list, array_keys($attribute['value_list']));
}
// count product per attribute-value-id
$result_count = array();
if(sizeof($att_value_id_list)) {
$query = $this->db->runQuery("
SELECT
pro_att.`attr_value_id`,
COUNT(DISTINCT ".TB_PRODUCT_CATEGORY.".pro_id) AS numPro
FROM ".TB_PRODUCT_ATTRIBUTE." pro_att
LEFT JOIN ".TB_PRODUCT_CATEGORY." ON pro_att.pro_id = ".TB_PRODUCT_CATEGORY.".pro_id
WHERE pro_att.pro_id IN (".join(',', $list_product_ids ).")
AND pro_att.`attr_value_id` IN (".join(",", $att_value_id_list).")
GROUP BY pro_att.`attr_value_id` ");
//lay ket qua dem
foreach ($this->db->fetchAll($query) as $rs ){
$result_count[$rs['attr_value_id']] = $rs['numPro'];
}
}
$final_result = [];
foreach ($category_attributes as $attribute) {
$new_value_list = [];
foreach ($attribute['value_list'] as $_value_id => $_value_info) {
$pro_count = isset($result_count[$_value_id]) ? $result_count[$_value_id] : 0;
if($pro_count) {
$_value_info['pro_count'] = $pro_count;
$new_value_list[$_value_id] = $_value_info;
}
}
$attribute['value_list'] = $new_value_list;
$final_result[] = $attribute;
}
return $final_result;
}
// 15-07-2020 get all attributes and values for a list of category
protected function getAttributesForCategory(array $category_ids){
if(!sizeof($category_ids)) {
return array();
}
$final_result = array();//array to return
$category_att_id_list = array();
$attribute_array = array(); //keep & re-order attribute
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($category_ids, 'int');
$query = $this->db->runQuery("
SELECT `attr_id`, `ordering` FROM `idv_attribute_category`
WHERE `category_id` IN (". $parameterized_ids .") AND `status` = 1 ", $bind_types, $category_ids);
foreach ($this->db->fetchAll($query) as $rs ){
$category_att_id_list[] = $rs['attr_id'];
$attribute_array[$rs['attr_id']] = $rs['ordering'];
}
if(!sizeof($category_att_id_list)) {
return array();
}
$att_value_id_list = array();
$attribute_info = array();
$attribute_att_value_item = array();
$attribute_value_info = array();
$query = $this->db->runQuery("
SELECT
attval.id ,
attval.attributeId ,
attval.value ,
attval.description ,
attval.api_key ,
attval.value_en ,
att.attribute_code ,
att.filter_code,
att.name ,
att.ordering
FROM ".TB_ATTRIBUTE_VALUE." attval
LEFT JOIN ".TB_ATTRIBUTE." att ON attval.attributeId = att.id
WHERE att.id IN (".join(',', $category_att_id_list).") AND att.isSearch = 1
ORDER BY attval.ordering DESC ");
foreach ($this->db->fetchAll($query) as $rs ){
$att_value_id = $rs['id'];
$att_id = $rs['attributeId'];
$att_value_id_list[] = $att_value_id;
$attribute_att_value_item[$att_id][] = $att_value_id; //in ordering
$attribute_info[$att_id] = array(
"id" => $att_id,
"name" => $rs['name'],
"code" => $rs['attribute_code'],
"filter_code" => $rs['filter_code'],
);
$attribute_value_info[$att_value_id] = array(
"id" => $att_value_id,
"name" => $rs['value'],
"description" => $rs['description'],
"api_key" => $rs['api_key'],
);
}
//cho ra ket qua
arsort($attribute_array); //sap xep thu tu cua attribute theo ordering
foreach($attribute_array as $att_id => $att_order){
$this_attribute_atr_value_item = array();
if(isset($attribute_att_value_item[$att_id])) {
foreach($attribute_att_value_item[$att_id] as $att_value_id){
$this_attribute_atr_value_item[$att_value_id] = array(
"id" => $att_value_id,
"info" => $attribute_value_info[$att_value_id],
);
}
}
if(isset($attribute_info[$att_id])) {
$info = $attribute_info[$att_id];
$info['value_list'] = $this_attribute_atr_value_item;
$final_result[] = $info;
}
}
return $final_result;
}
public function priceList($current_max_price, $current_min_price, array $list_product_ids, array $category_price_range) {
if(!sizeof($list_product_ids)) {
return [];
}
$total_prices = sizeof($category_price_range);
if( ! $total_prices ){
return [];
}
//pad head and tail with 0
array_unshift($category_price_range, 0);
$category_price_range[] = 0;
$result = array();
$queries = [];
for($i = 0; $i <= $total_prices ; $i++){
$range_key = md5($category_price_range[$i] .'-'. $category_price_range[$i+1] );
$queries[] = $this->buildRangeQuery($list_product_ids, $range_key, $category_price_range[$i], $category_price_range[$i+1]);
$min_price = intval($category_price_range[$i]);
$max_price = intval($category_price_range[$i+1]);
$is_selected = ($max_price == $current_max_price && $min_price == $current_min_price );
$result[$range_key] = array(
"min" => $min_price ,
"max" => $max_price,
"count" => 0 ,
"is_selected" => ($is_selected) ? 1 : 0 ,
);
}
$query = $this->db->runQuery(" SELECT `range_key`, `total` FROM ( ".join(' UNION ALL ', $queries)." ) AS tmp");
foreach ($this->db->fetchAll($query) as $rs) {
$result[$rs['range_key']]['count'] = $rs['total'];
}
// get only range with product-count > 0
return array_values(array_filter( $result , function ($item){
return $item['count'] > 0;
}));
}
private function buildRangeQuery(array $list_product_ids, $range_key, $minPrice, $maxPrice){
$minPrice = (int) $minPrice;
$maxPrice = (int) $maxPrice;
if($minPrice < 10) $minPrice = 10;
$new_price_range = " AND `id` IN (".join(',', $list_product_ids ).") ";
if($maxPrice > 0 && $minPrice > 0){
$new_price_range .= " AND ( `price` BETWEEN '".$minPrice."' AND '".$maxPrice."' ) ";
}elseif($maxPrice > 0){
$new_price_range .= " AND `price` < '".$maxPrice."' ";
}elseif($minPrice > 0 && $maxPrice == 0){
$new_price_range .= " AND `price` >='".$minPrice."' ";
}
return " SELECT '".$range_key."' AS `range_key`, COUNT(`id`) AS total
FROM ".TableName::PRODUCT."
WHERE 1 ".$new_price_range;
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Components\Product\AdminController\AProductHotController;
use Hura8\Database\iConnectDB;
use Hura8\System\Config;
class ProductHotModel
{
/* @var $db iConnectDB */
protected $db;
protected $tb_product = "tb_product";
protected $tb_product_hot = "tb_product_hot";
public function __construct() {
$this->db = get_db();
}
public function updateProductHot($pro_id, array $new_types) {
$this->db->runQuery("DELETE FROM " . $this->tb_product_hot . " WHERE `pro_id` = ? ", ['d'], [ $pro_id ]);
$this->db->update(
$this->tb_product,
[
"hot_type" => '',
],
[
'id' => $pro_id,
]
);
$config_hottype = Config::getProductHotTypeList();
//insert what good
$batch_insert = array();
$accepted_types = array();
foreach ($new_types as $hot_type) {
if (!isset($config_hottype[$hot_type])) continue;
$batch_insert[] = [
"pro_id" => $pro_id,
"hot_type" => $hot_type,
];
$accepted_types[] = $hot_type;
}
if (sizeof($batch_insert)) {
$this->db->bulk_insert($this->tb_product_hot, $batch_insert );
$this->db->update(
$this->tb_product,
[
"hot_type" => join(",", $accepted_types),
],
[
"id" => $pro_id,
]
);
}
return true;
}
public function getProductHot(array $product_ids) {
if(!sizeof($product_ids)){
return [];
}
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($product_ids, 'int');
$query = $this->db->runQuery(
"SELECT `pro_id`, `hot_type` FROM ".$this->tb_product_hot." WHERE `pro_id` IN (".$parameterized_ids.") ",
$bind_types,
$product_ids
);
$result = [];
foreach ( $this->db->fetchAll($query) as $rs ) {
$result[$rs['pro_id']][] = $rs['hot_type'];
}
return $result;
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Database\MysqlValue;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
class ProductImageModel extends aEntityBaseModel
{
protected $product_id = 0;
/* @var ProductModel $objProductModel */
protected $objProductModel;
public function __construct($product_id = 0) {
parent::__construct('product_image');
$this->product_id = $product_id;
$this->objProductModel = new ProductModel();
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function setProduct($product_id) {
$this->product_id = $product_id;
}
public function countProductImage($product_id) {
$query = $this->db->runQuery("SELECT COUNT(*) AS total FROM `".$this->tb_entity."` WHERE `pro_id` = ? ", ['d'], [ $product_id]);
if($info = $this->db->fetchAssoc($query)) {
return $info['total'];
}
return 0;
}
protected function _buildQueryOrderBy($sort_by = "new")
{
return " `ordering` DESC, `id` DESC ";
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"q" => "",
"letter" => "",
"status" => 0,
);*/
$catCondition = [" AND `pro_id` = ? "];
$bind_types = ['d'];
$bind_values = [$this->product_id];
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
protected function getProductMainImage() {
$query = $this->db->runQuery(
"SELECT `id` FROM `".$this->tb_entity."` WHERE `pro_id` = ? AND `is_main` = 1 LIMIT 1",
['d'], [ $this->product_id]
);
return $this->db->fetchAssoc($query);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
class ProductInfoModel extends aEntityBaseModel
{
protected $richtext_fields = [
'description',
'spec',
'instruction',
];
public function __construct() {
parent::__construct('product_info', '', null, $this->richtext_fields);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
return null;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\System\Model\EntityLanguageModel;
use Hura8\Interfaces\EntityType;
class ProductLanguageModel extends EntityLanguageModel
{
protected $richtext_fields = [
'description',
'spec',
'instruction',
];
public function __construct() {
parent::__construct(EntityType::PRODUCT, '', $this->richtext_fields);
}
}

View File

@@ -0,0 +1,410 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\iEntityModel;
use Hura8\System\Config;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
class ProductModel extends aEntityBaseModel implements iEntityModel
{
static $url_module = "product";
static $url_view = "detail";
static $url_type = "product:detail";
protected $tb_product_per_category = 'tb_product_per_category';
protected $tb_collection_product = "tb_collection_product";
protected $tb_product_info = 'tb_product_info';
protected $tb_product_hot = "tb_product_hot";
protected $richtext_fields = [
//'special_offer',
];
public function __construct() {
parent::__construct(
EntityType::PRODUCT,
"",
new ProductSearchModel(),
$this->richtext_fields
);
}
protected function extendedFilterOptions() : array
{
return [
"price" => array('max' => 0, 'min'=> 0),
"brand" => array(), // array(1,2,3,)
"collection" => array(), // array(1,2,3,)
"supplier" => array(), // array(1,2,3,)
"rating" => array(), // array(1,2,3,)
"category" => array(), // array(1,2,3,)
"status" => array(), // array(1,2,3,)
"hotType" => array(),// array(saleoff | not | new)
"attribute" => array(), // array(1,2,3,)
"promotion" => "",
"other_filter" => array(), // array(in-stock, has-promotion etc...)
"spec_group_id" => array(), // array(1,2,3,)
];
}
public function getProductInfoBySKU(string $sku) {
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` basic, `".$this->tb_product_info."` info
WHERE basic.`id` = info.`id` AND basic.sku = ?
LIMIT 1 ",
['d'], [$sku]
);
if( $item_info = $this->db->fetchAssoc($query)){
return $item_info;
}
return false;
}
public function getFullInfo($id) {
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` basic, `".$this->tb_product_info."` info
WHERE basic.`id` = info.`id` AND basic.id = ?
LIMIT 1 ",
['d'], [$id]
);
if( $item_info = $this->db->fetchAssoc($query)){
return $item_info;
}
return null;
}
public function getProductCategoryList($pro_id) {
$query = $this->db->runQuery(
"SELECT `category_id` FROM ".$this->tb_product_per_category." WHERE `item_id` = ? ",
['d'], [ $pro_id ]
) ;
$pro_cat = [];
foreach ( $this->db->fetchAll($query) as $rs ) {
$pro_cat[] = $rs['category_id'];
}
return $pro_cat;
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$where_query = [];
$bind_types = [];
$bind_values = [];
//-------------------
//other filters
if( array_key_exists("other_filter", $filter_condition) && sizeof($filter_condition["other_filter"])) {
foreach ($filter_condition["other_filter"] as $_filter) {
switch ($_filter) {
case "in-stock";
$where_query[] = " AND quantity > 0 ";
break;
case "has-vat";
$where_query[] = " AND `has_vat` = 1 ";
break;
case "out-stock";
$where_query[] = " AND `quantity` = 0 ";
break;
case "has-market-price";
$where_query[] = " AND `market_price` > 0 ";
break;
case "no-price";
$where_query[] = " AND `price` = 0 ";
break;
case "no-warranty";
$where_query[] = " AND LENGTH(`warranty`) < 2 ";
break;
case "no-sku";
$where_query[] = " AND LENGTH(`sku`) < 2 ";
break;
case "has-config";
$where_query[] = " AND `config_count` > 0 ";
break;
case "no-image";
$where_query[] = " AND `image_count` = 0 ";
break;
case "no-category";
$where_query[] = " AND `category` = '0' ";
break;
case "display-off";
$where_query[] = " AND `status`=0 ";
break;
case "display-on";
$where_query[] = " AND `status`=1 ";
break;
case "has-promotion":
$where_query[] = " AND LENGTH(`special_offer`) < 5 ";
break;
//...add more
}
}
}
//- brand id or ids or brand_indexes
if( array_key_exists("brand", $filter_condition) && sizeof($filter_condition["brand"])) {
$condition = array();
foreach ($filter_condition["brand"] as $brand_id) {
if(!intval($brand_id)) continue;
$condition[] = " brand_id = ? ";
$bind_types[] = 'd';
$bind_values[] = $brand_id;
}
if(sizeof($condition)) {
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
}
//- collection id or ids
if( array_key_exists("collection", $filter_condition) && sizeof($filter_condition["collection"])) {
$condition = array();
foreach ($filter_condition["collection"] as $_id) {
if(!intval($_id)) continue;
$condition[] = " `id` IN ( SELECT `product_id` FROM ".$this->tb_collection_product." WHERE `collection_id` = ? ) ";
$bind_types[] = 'd';
$bind_values[] = $_id;
}
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
//- rating: 1-> 5
if( array_key_exists("rating", $filter_condition) && sizeof($filter_condition["rating"])) {
$condition = array();
foreach ($filter_condition["rating"] as $_id) {
$condition[] = " `rating` = ? ";
$bind_types[] = 'd';
$bind_values[] = $_id;
}
$where_query[] = " AND ( ".join(" OR ", $condition)." )";
}
//- category id or ids
if( array_key_exists("category", $filter_condition) && sizeof($filter_condition["category"])) {
$objProductCategoryModel = new ProductCategoryModel();
$condition = array();
foreach ($filter_condition["category"] as $cat_id) {
$cat_info = $objProductCategoryModel->getInfo($cat_id);
if(!$cat_info) continue;
if($cat_info["is_parent"]) {
$childListId = ($cat_info["child_ids"]) ?: '0';
$condition[] = " `category_id` IN (".$childListId .") ";
}else{
$condition[] = " `category_id` = ? ";
$bind_types[] = 'd';
$bind_values[] = $cat_id;
}
}
if(sizeof($condition)) {
$where_query[] = " AND `id` IN ( SELECT DISTINCT `item_id` FROM ".$this->tb_product_per_category." WHERE " . join(" OR ", $condition) . " )";
}
}
// spec_group_id
if( array_key_exists("spec_group_id", $filter_condition) && sizeof($filter_condition["spec_group_id"])) {
$condition = array();
foreach ($filter_condition["spec_group_id"] as $_id) {
if(!$_id) continue;
$condition[] = " `spec_group_id` = ? ";
$bind_types[] = 'd';
$bind_values[] = $_id;
}
$where_query[] = " AND ( ".join(" OR ", $condition )." ) ";
}
//- hotType: saleoff | not | new | or combination of types
if( array_key_exists("hotType", $filter_condition) && sizeof($filter_condition["hotType"])) {
$config_hottype = Config::getProductHotTypeList();
$condition = array();
foreach ($filter_condition["hotType"] as $_id) {
if(!array_key_exists($_id, $config_hottype)) continue;
$condition[] = " `hot_type` = ? ";
$bind_types[] = 's';
$bind_values[] = $_id;
}
if(sizeof($condition)) {
$where_query[] = " AND `id` IN (SELECT `pro_id` FROM ".$this->tb_product_hot." WHERE 1 AND ( ".join(" OR ", $condition)." ) ) ";
}
}
//- attribute values
/*
if( array_key_exists("attribute", $filter_condition) && sizeof($filter_condition["attribute"])) {
$filter_attr_value_list = $filter_condition["attribute"];
//filter = attr_value_1-attr_value_2-attr_value_3,
$query_attr_id = [];
$count_filter = 0;
foreach($filter_attr_value_list as $attr_id){
$attr_id = (int) $attr_id;
if($attr_id) {
$query_attr_id[] = $attr_id;
$count_filter ++;
}
}
$product_filter_id_match = array();
if(sizeof($query_attr_id)) {
$query = $this->db->runQuery("
SELECT DISTINCT pro_id , COUNT(*) as num_pro
FROM ".TB_PRODUCT_ATTRIBUTE."
WHERE attr_value_id IN (".join(',', $query_attr_id).")
GROUP BY pro_id
HAVING num_pro = ".$count_filter."
LIMIT 10000
");
foreach ( $this->db->fetchAll($query) as $rs ) {
$product_filter_id_match[] = $rs["pro_id"];
}
}
$where_query[] = (sizeof($product_filter_id_match)) ? " AND `id` IN (".join(', ', $product_filter_id_match).") " : " AND `id` = 0 " ;
//xay lai url de back
if(!$this->objCategoryProduct) $this->objCategoryProduct = new CategoryProduct();
foreach($filter_attr_value_list as $value_id ){
$att_name = $this->objCategoryProduct->atrValueName($value_id);
$filterPath["attribute"][] = array(
"id" => $value_id,
"name" => $att_name,
);
$filter_messages[] = [
'title' => $att_name,
'reset' => Url::buildUrl($this->_request_url, ['filter' => join(FILTER_VALUE_SEPARATOR, remove_item_from_array($filter_condition["attribute"], $value_id))] ),
];
}
}
*/
//- attribute values
if( array_key_exists("attribute", $filter_condition) && sizeof($filter_condition["attribute"])) {
$filter_attr_value_list = $filter_condition["attribute"];
$attr_values_per_attribute = $this->groupAttributeValuesByAttributeId($filter_attr_value_list);
$having_match_count = sizeof(array_keys($attr_values_per_attribute));
$attribute_info_list = $this->groupAttributeListInfo(array_keys($attr_values_per_attribute));
$attr_where_query = [];
foreach ($attr_values_per_attribute as $_att_id => $_att_value_list) {
// if same attribute : find products that match either ONE of these values (not match ALL values)
$_att_info = $attribute_info_list[$_att_id];
if (!$_att_info) continue;
// this attribute requires all products must have ALL selected values
if ($_att_info['value_match_all'] && sizeof($_att_value_list) > 1) {
$_product_id_match_all = $this->getProductMatchAllAttributeValueIds($_att_value_list);
if (sizeof($_product_id_match_all) > 0) {
$attr_where_query[] = " ( `id` IN (" . join(",", $_product_id_match_all) . ") ) ";
} else {
$attr_where_query[] = " ( `id` = -1 ) ";
}
} else {
$attr_where_query[] = " ( SELECT DISTINCT `pro_id` FROM " . TB_PRODUCT_ATTRIBUTE . " WHERE (" .
join(" OR ", array_map(function ($_item) { return " `attr_value_id` = '" . intval($_item['id']) . "' "; }, $_att_value_list))
. " ) ) ";
}
}
$product_filter_id_match = array();
if (sizeof($attr_where_query)) {
$query = $this->db->runQuery("
SELECT `pro_id` , COUNT(*) AS num_pro
FROM ( " . join(" UNION ALL ", $attr_where_query) . " ) AS tpm_tb
GROUP BY pro_id
HAVING num_pro = " . $having_match_count . "
LIMIT 10000
");
foreach ($this->db->fetchAll($query) as $rs) {
$product_filter_id_match[] = $rs["pro_id"];
}
}
$where_query[] = (sizeof($product_filter_id_match)) ? " AND " . TB_PRODUCT_LIGHT . ".`id` IN (" . join(', ', $product_filter_id_match) . ") " : " AND " . TB_PRODUCT_LIGHT . ".`id` = 0 ";
}
//- price range
if(
isset($filter_condition["price"])
&& sizeof($filter_condition["price"])
&& ($filter_condition["price"]['min'] > 0 || $filter_condition["price"]['max'] > 0)
) {
//limit by price range
$maxPrice = DataClean::makeInputSafe($filter_condition["price"]['max'], DataType::INTEGER);
$minPrice = DataClean::makeInputSafe($filter_condition["price"]['min'], DataType::INTEGER);
$price_range_query = '';
if($maxPrice > 0 && $minPrice > 0){
$price_range_query = " ( `price` BETWEEN '".$minPrice."' AND '".$maxPrice."' ) ";
}else if($maxPrice > 0){
$price_range_query = " `price` < '".$maxPrice."' ";
}else if($minPrice > 0){
$price_range_query = " `price` >='".$minPrice."' ";
}
$where_query[] = " AND ". $price_range_query;
}
// ------------
return array( join(" ", $where_query), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class ProductSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
"price" => "tb_product.price",
"ranking" => "tb_product.ranking",
"status" => "tb_product.status",
"brand" => "tb_product.brand_id",
];
private $fulltext_fields = [
"category_keywords" => ["tb_product_category.title", ],
"product_keywords" => ["tb_product.sku", "tb_product.title", "tb_product.model", "tb_product.related_keywords"],
];
public function __construct()
{
parent::__construct(
"tb_product",
$this->fulltext_fields,
$this->filter_fields
);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
class ProductSpecGroupAttributeModel extends aEntityBaseModel
{
protected $tb_product = 'tb_product';
protected $tb_product_spec = 'tb_product_spec';
protected $tb_product_spec_group = 'tb_product_spec_group';
protected $tb_product_spec_group_record = 'tb_product_spec_group_record';
public function __construct() {
parent::__construct('product_spec_group_record');
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getSpecGroupAttribute($group_id)
{
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_product_spec_group_record."` WHERE `group_id` = ? ORDER BY `ordering` ASC ",
['d'], [$group_id]
);
return $this->db->fetchAll($query);
}
protected function updateGroupAttributeCount($group_id) {
$this->db->runQuery("UPDATE `".$this->tb_product_spec_group."` SET
`attribute_count` = (SELECT COUNT(*) FROM `".$this->tb_product_spec_group_record."` WHERE `group_id` = ? )
WHERE `id` = ? LIMIT 1 ", ['d', 'd' ], [ $group_id, $group_id ]);
}
protected function buildQueryCondition(array $condition)
{
// TODO: Implement buildQueryCondition() method.
}
protected function buildOrderByCondition($order_by)
{
// TODO: Implement buildOrderByCondition() method.
}
protected function _buildQueryConditionExtend(array $extend_filter_conditions) : ?array
{
return null;
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
class ProductSpecGroupModel extends aEntityBaseModel
{
protected $tb_product = 'tb_product';
protected $tb_product_spec_group = 'tb_product_spec_group';
protected $tb_product_attribute = 'tb_product_attribute';
public function __construct() {
parent::__construct('product_spec_group');
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getSpecGroupAttribute($group_id) {
$objProductSpecGroupAttributeModel = new ProductSpecGroupAttributeModel();
return $objProductSpecGroupAttributeModel->getSpecGroupAttribute($group_id);
}
protected function clearProductAttributes($product_id) {
$this->db->runQuery(
"DELETE FROM `".$this->tb_product_attribute."` WHERE `pro_id` = ? ",
[ 'd' ], [ $product_id ]
);
}
public function clearProductSpecGroup($product_id) {
$this->clearProductAttributes($product_id);
$product_spec_group_id = $this->getProductSpecGroupId($product_id);
// update
$this->db->runQuery(
"UPDATE `".$this->tb_product."` SET
`spec_group_id` = 0
WHERE `id` = ? LIMIT 1 ",
[ 'd' ], [ $product_id ]
);
$this->updateSpecGroupProductCount($product_spec_group_id);
return true;
}
public function setProductSpecGroup($product_id, $new_group_id)
{
$current_group_id = $this->getProductSpecGroupId($product_id);
if($current_group_id != $new_group_id) {
// update
$this->db->runQuery(
"UPDATE `".$this->tb_product."` SET
`spec_group_id` = ?
WHERE `id` = ? LIMIT 1 ",
[ 'd', 'd' ], [ $new_group_id, $product_id ]
);
$this->updateSpecGroupProductCount($new_group_id);
$this->clearProductAttributes($product_id);
return true;
}
return false;
}
protected function updateSpecGroupProductCount($group_id) {
$this->db->runQuery(
"UPDATE `".$this->tb_product_spec_group."` SET
`product_count` = (SELECT COUNT(*) FROM ".$this->tb_product." WHERE `spec_group_id` = ? )
WHERE `id` = ? LIMIT 1 ",
[ 'd', 'd' ], [ $group_id, $group_id ]
);
}
public function getProductSpecGroupInfo($product_id)
{
$product_spec_group_id = $this->getProductSpecGroupId($product_id);
return ($product_spec_group_id) ? $this->getSpecGroupInfo($product_spec_group_id) : false ;
}
public function getProductSpecGroupId($product_id)
{
$query = $this->db->runQuery("SELECT `spec_group_id` FROM `".$this->tb_product."` WHERE `id` = ? LIMIT 1 ", ['d'], [$product_id]);
if ($info = $this->db->fetchAssoc($query) ) {
return $info['spec_group_id'] ;
}
return 0;
}
public function getSpecGroupInfo($group_id)
{
$query = $this->db->runQuery("SELECT * FROM `".$this->tb_product_spec_group."` WHERE `id` = ? ", ['d'], [$group_id]);
return $this->db->fetchAssoc($query);
}
protected function buildQueryCondition(array $condition)
{
$catCondition = [];
//Tim kiem theo tu khoa
if(isset($condition["q"]) && $condition["q"]){
$catCondition[] = " AND `title` LIKE '%".$this->db->escape($condition["q"])."%' ";
}
return join(" ", $catCondition);
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$where_condition = "";
$bind_types = [];
$bind_values = [];
return [
$where_condition,
$bind_types,
$bind_values
];
}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Components\Product\Controller\ProductController;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
class ProductVariantModel extends aEntityBaseModel
{
protected $product_id = 0;
protected $tb_product = "tb_product";
protected $tb_variant_option_sample = "tb_product_variant_option_sample";
public function __construct($product_id) {
parent::__construct('product_variant');
$this->product_id = $product_id;
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getProductVariantOption($product_id){
$query = $this->db->runQuery("SELECT `variant_option` FROM `".$this->tb_product."` WHERE `id` = ? LIMIT 1", ['d'], [ $product_id ]) ;
if($rs = $this->db->fetchAssoc($query)) {
return ($rs['variant_option']) ? \json_decode($rs['variant_option'], true) : null;
}
return null;
}
//use a product's variant-option to create a sample, so next product can select without recreate from beginning
public function createVariantOptionSample($use_from_pro_id, $sample_title) {
if( !$use_from_pro_id || strlen($sample_title) < 3 ) return false;
$pro_variant_option = $this->getProductVariantOption($use_from_pro_id);
if(!$pro_variant_option) {
return false;
}
$pro_variant_option_index = md5(\json_encode($pro_variant_option));
$check_duplicate = $this->db->runQuery(
"SELECT `id` FROM ".$this->tb_variant_option_sample." WHERE `variant_option_index` = ? LIMIT 1 ",
['s'], [ $pro_variant_option_index ]
);
if($this->db->fetchAssoc($check_duplicate)) {
return false;
}
// ok to save
$this->db->insert(
$this->tb_variant_option_sample,
[
"title" => $sample_title,
"variant_option" => \json_encode($pro_variant_option),
"variant_option_index" => $pro_variant_option_index,
"create_time" => CURRENT_TIME,
]
);
return true;
}
public function getVariantOptionSample() {
$query = $this->db->runQuery("SELECT * FROM ".$this->tb_variant_option_sample." ORDER BY `id` DESC LIMIT 500 ");
return $this->db->fetchAll($query);
}
public function getProductVariantPriceRange(){
$result = [
"sale_price" => [
"min" => 0,
"max" => 0,
],
"market_price" => [
"min" => 0,
"max" => 0,
],
];
$query = $this->db->runQuery(
" SELECT `sale_price`, `market_price`, `extend` FROM `".$this->tb_entity."` WHERE `product_id` = ? ",
['d'], [$this->product_id]
);
foreach ( $this->db->fetchAll($query) as $info) {
// find min
if($info["sale_price"] > 0 && ( $info["sale_price"] < $result["sale_price"]["min"] || $result["sale_price"]["min"] == 0 ) ) {
$result["sale_price"]["min"] = $info["sale_price"];
}
// find max
if($info["sale_price"] > 0 && $info["sale_price"] > $result["sale_price"]["max"] ) {
$result["sale_price"]["max"] = $info["sale_price"];
}
// market_price
$market_price = $info["market_price"];
if($info['extend']) {
$extend = unserialize($info['extend']);
if(isset($extend['market_price'])) $market_price = clean_price($extend['market_price'], "vnd");
}
if($market_price > 0 && ( $market_price < $result["market_price"]["min"] || $result["market_price"]["min"] == 0 ) ) {
$result["market_price"]["min"] = $market_price;
}
if($market_price > 0 && $market_price > $result["market_price"]["max"] ) {
$result["market_price"]["max"] = $market_price;
}
}
return $result;
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$where_condition = " AND `product_id` = ? ";
$bind_types = ["d"];
$bind_values = [$this->product_id];
return [
$where_condition,
$bind_types,
$bind_values
];
}
protected function _buildQueryOrderBy($sort_by = "new")
{
return " `ordering` DESC, `id` DESC ";
}
protected function formatItemInList(array $item_info): array
{
$info = $item_info;
if($item_info['attribute']) $info['attribute'] = \json_decode($item_info['attribute'], true);
return $info;
}
protected function updateProductVariantCount() {
$this->db->runQuery(
"UPDATE ".$this->tb_product." SET
`config_count` = ( SELECT COUNT(*) AS total FROM `".$this->tb_entity."` WHERE `product_id` = ? AND `status` = 1 )
WHERE `id` = ? ",
['d', 'd'], [ $this->product_id, $this->product_id]
) ;
}
protected function formatItemInfo(array $item_info) : ?array
{
$info = $item_info;
if($info['attribute']) $info['attribute'] = \json_decode($info['attribute'], true);
return $info;
}
}

View File

@@ -0,0 +1,503 @@
<?php
namespace Hura8\Database;
use Hura8\Traits\ClassCacheTrait;
final class ConnectDB implements iConnectDB
{
use ClassCacheTrait;
private $debug = false;
private static $instance = [];
private static $cnn_props = [];
/* @var $connection \mysqli */
private $connection;
private static $traces = [];
private $db_id = '';
private function __construct($db_id = 'main', $debug = false)
{
// enable database debug
if($debug) $this->debug = $debug; // (defined('ENABLE_DB_DEBUG') && ENABLE_DB_DEBUG);
if($db_id) $this->db_id = $db_id;
if(!sizeof(self::$cnn_props)) {
self::$cnn_props = self::setConnectionSettings();
}
}
private static function setConnectionSettings() {
return static::getCache("getConnectionSettings", function (){
$db_file = CONFIG_DIR.'/db.php';
if(file_exists($db_file)) {
return include $db_file;
}
return [];
});
}
/**
* @param string $table
* @return array
*/
public function getTableDefaultItemInfo($table) {
$column_info = $this->getTableInfo($table);
$default_info = [];
foreach ($column_info as $field => $info) {
$default_info[$field] = $info['COLUMN_DEFAULT'];
}
return $default_info;
}
//28-07-2015 get all columns of a table
public function getTableInfo($table) {
return self::getCache('getTableInfo-'.$table, function () use ($table) {
$db_props = $this->getConnectionProps();
if(!$db_props) {
return false;
}
$database = $db_props['db'] ?? null;
if(!$database) {
return false;
}
$query = $this->runQuery(
"SELECT
`COLUMN_NAME` ,
COLUMN_DEFAULT,
ORDINAL_POSITION,
COLUMN_DEFAULT,
DATA_TYPE,
CHARACTER_MAXIMUM_LENGTH,
COLUMN_TYPE,
COLUMN_KEY,
EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE `TABLE_SCHEMA` = ? AND`TABLE_NAME` = ? ",
['s', 's'], [$database, $table]
);
$output = [];
foreach( $this->fetchAll($query) as $row){
$output[$row['COLUMN_NAME']] = $row;
}
return $output;
});
}
protected function setTrace($query, $start_time, $msg = ''){
static::$traces[] = [
"msg" => $msg,
"query" => $query,
"start_time" => $start_time,
"total_time" => round($this->getCurrentTime() - $start_time, 5),
];
}
public static function getTraces() {
$query_count = sizeof(static::$traces);
$query_time = array_sum(array_map(function ($item){ return $item['total_time'];}, static::$traces)) ;
return [
'query_count' => $query_count,
'query_time' => $query_time,
'list' => static::$traces,
];
}
private function __clone(){
//
}
/**
* @param string $db_id
* @param bool $debug
* @return iConnectDB
*/
public static function getInstance(string $db_id = '', bool $debug = false) {
if( ! $db_id ) $db_id = 'main';
if(!isset(ConnectDB::$instance[$db_id])) {
ConnectDB::$instance[$db_id] = new self($db_id, $debug);
}
return ConnectDB::$instance[$db_id];
}
//close all connections
public static function close() {
foreach (ConnectDB::$instance as $db_id => $cnn) {
$cnn->disconnect();
}
// reset
ConnectDB::$instance = [];
}
public function isConnected(): bool {
return ($this->connection instanceof \mysqli);
}
private function getConnectionProps() {
return self::$cnn_props[$this->db_id] ?? null;
}
private function connect()
{
// already connect
if($this->connection instanceof \mysqli) {
return true;
}
// connection props not set
$db_props = $this->getConnectionProps();
if(!$db_props) {
return false;
}
$host = $db_props['host'] ?? null;
$user = $db_props['user'] ?? null;
$pass = $db_props['pass'] ?? null;
$database = $db_props['db'] ?? null;
$db_charset = $db_props['charset'] ?? 'latin1';
if(!$host || !$user || !$pass || !$database) {
return false;
}
try {
//create the object
$cnn = \mysqli_init();
$cnn->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5);
//specify the read timeout
if (!defined('MYSQLI_OPT_READ_TIMEOUT')) {
define ('MYSQLI_OPT_READ_TIMEOUT', 11);
}
$cnn->options(MYSQLI_OPT_READ_TIMEOUT, 10);
if($cnn->real_connect($host, $user, $pass)){
$cnn->select_db($database);
$cnn->query("SET NAMES ".$db_charset); // UTF8|latin1
$cnn->query("SET sql_mode=''"); //for old version to work with mysql 5.7
//mysqli_select_db($this->connection, $database);
//mysqli_query($this->connection, "SET NAMES UTF8") ;//set utf8 if database using utf-8 encode
//mysqli_query($this->connection, "SET NAMES latin1") ;//set back to latin1
//mysqli_query($this->connection, "SET sql_mode=''") ;//for old version to work with mysql 5.7
$this->connection = $cnn;
return true;
}
//throw new \Exception('Unable to connect');
return false;
}catch (\Exception $e) {
//die($e->getMessage());
echo $e->getMessage();
return false;
}
}
public function disconnect()
{
if($this->connection instanceof \mysqli) {
$this->connection->close();
$this->connection = null;
}
}
public function ping() {
if($this->connection) {
$this->connection->ping();
}
}
/**
* @param string $query
* @param array $bind_types
* @param array $bind_values
* @param bool $get_affected_row_or_id get the affected row or newly insert-id from the query, default return the mysqli_result
* @return \mysqli_result | false | int
*/
public function runQuery($query, array $bind_types=[], array $bind_values=[], $get_affected_row_or_id = false)
{
$start_time = $this->getCurrentTime();
// connect on demand
if(!$this->connect()) {
if($this->debug) $this->setTrace($query, $start_time, 'runQuery: Connection fails');
return false;
}
$stmt = $this->connection->prepare( $query );
if(!$stmt) {
//throw new \Exception($stmt->error);
//throw new \Exception($this->connection->error);
if($this->debug) {
$this->setTrace($query, $start_time, 'runQuery: '.$this->connection->error);
die("runQuery error: ".$this->connection->error.". Query: ".$query);
}
return false;
}
if(sizeof($bind_types) && sizeof($bind_types) == sizeof($bind_values)) {
if(!$stmt->bind_param(join('', $bind_types), ...$bind_values)) {
if($this->debug) {
$this->setTrace($query, $start_time, 'runQuery: bind_param '.$this->connection->error);
die('runQuery: bind_param '.$this->connection->error);
}
return false;
}
}
if(!$stmt->execute()) {
if($this->debug) {
$this->setTrace($query, $start_time, 'runQuery: execute '.$this->connection->error);
die("runQuery error: ".$this->connection->error.". Query: ".$query);
}
return false;
}
if($this->debug) $this->setTrace($query, $start_time, '');
if($get_affected_row_or_id) {
return $stmt->insert_id ?: $stmt->affected_rows;
}
// default
return $stmt->get_result();
}
private function getCurrentTime(){
return microtime(true);
}
/**
* @deprecated Unsafe, make sure all variables are properly escaped
*/
public function multi_query(array $array_query){
if(!sizeof($array_query)) return false;
$start_time = $this->getCurrentTime();
// late connect
if(!$this->connect()) {
if($this->debug) $this->setTrace(join("; ", $array_query), $start_time, 'multi_query: Connection fails');
return false;
}
$multi_query = join(";", $array_query);
$multi_query = str_replace("\n", " ", $multi_query);
//remove double ; if exist
$multi_query = preg_replace("/;(\s+)?;/i", ";", $multi_query);
$set = 0;
$list = [];
if(mysqli_multi_query($this->connection, $multi_query)){
// flush multi_queries, so any query after can run
do {
$set ++;
/* store first result set */
if ($result = mysqli_store_result($this->connection)) {
/*while ($row = mysqli_fetch_assoc($result)) {
$list[$set][] = $row;
}*/
$list[$set] = $this->fetchAll($result);
mysqli_free_result($result);
}
} while (mysqli_more_results($this->connection) && mysqli_next_result($this->connection));
}
if($this->debug) $this->setTrace(join(";", $array_query), $start_time);
return $list;
}
// a simple utitily method which we use very frequently
public function getItemInfo($table_name, $id_value, $id_field = 'id'){
return $this->select(
$table_name,
[],
[
$id_field => ["=", $id_value],
],
'',
1
);
}
//$table_name = 'table_abc'
/*$fields = array(
"field_name_1",
);*/
/*$where_condition = array(
"field_name_1" => ["=", "value_1"],
"field_name_2" => ["LIKE", "value_1"],
);*/
public function select($table_name, $fields =[], $where_condition=[], $order_by = '', $limit='1'){
// connect on demand
if(!$this->connect()) {
if($this->debug) $this->setTrace($table_name, 0, 'select: Connection fails');
return false;
}
$start_time = $this->getCurrentTime();
$check_fields = [];
if(sizeof($fields) && $fields[0] != '*') $check_fields = $fields;
$check_fields = array_merge($check_fields, array_keys($where_condition));
$cols_in_tables = $this->filterColumnInTable($table_name, $check_fields);
$bind_types = [];
$bind_values = [];
$condition_list = [];
$permitted_operator = [">", ">=", "<", "<=", "!=", "=", "BETWEEN", "LIKE", "IS"]; // https://dev.mysql.com/doc/refman/8.0/en/non-typed-operators.html
foreach ($where_condition as $field_name => $operator_value) {
// if any field invalid, stop the query
if(!in_array($field_name, $cols_in_tables)) {
echo "Invalid field_name ".$field_name;
return false;
};
// invalidate
if(!is_array($operator_value) || sizeof($operator_value) != 2) continue;
$operator = $operator_value[0];
if(!in_array($operator, $permitted_operator)) continue;
$value = $operator_value[1];
$condition_list[] = " `".$field_name."` ".$operator." ? ";
$bind_types[] = (is_int($value)) ? 'd' : 's';
$bind_values[] = $value;
}
$select_fields = (sizeof($fields) > 0) ? array_map(function ($field){ return "`".$field."`";}, $fields) : ["*"];
$order_by_query = ($order_by) ? "ORDER BY ".preg_replace("/[^a-z0-9\s_]/i", "", $order_by) : "";
$safe_limit = ($limit) ? preg_replace("/[^0-9\,\s]/i", "", $limit) : "1";
$where_condition = (sizeof($condition_list)) ? " AND " . join(" AND ", $condition_list) : "";
$query = "SELECT ".join(',', $select_fields)."
FROM `".$table_name."` WHERE 1 ".$where_condition."
".$order_by_query."
LIMIT ".$safe_limit;
$stmt = $this->connection->prepare( $query );
if(!$stmt) return false;
if(sizeof($bind_types)) {
$stmt->bind_param(join('', $bind_types), ...$bind_values);
}
$stmt->execute();
$result = $stmt->get_result();
if($this->debug) $this->setTrace($query, $start_time);
return ($safe_limit == '1') ? $result->fetch_assoc() : $result->fetch_all();
}
protected function filterColumnInTable($table_name, array $list_check_fields) {
$table_columns = $this->getTableInfo($table_name);
$safe_fields = [];
foreach ($list_check_fields as $field_name) {
// make sure the field exists
if(!array_key_exists($field_name, $table_columns)) continue;
$safe_fields[] = $field_name;
}
return $safe_fields;
}
//fetch array
/**
* @deprecated
*/
public function fetchArray(\mysqli_result $resource){
return $resource->fetch_array();
}
/**
* @param \mysqli_result | false $resource
* @return array|null
*/
public function fetchAssoc( $resource){
if(!$resource) {
//die("Resource failed: code fetchAssoc 122");
return null;
}
return $resource->fetch_assoc();
}
/**
* @param \mysqli_result | false $resource
* @return array|mixed
*/
public function fetchAll($resource){
if(!$resource) return [];
//some hosting does not enable this function, so this is the work-around
if( ! function_exists("mysqli_fetch_all")) {
$item_list = array();
while ($item = $this->fetchAssoc($resource)) {
$item_list[] = $item;
}
return $item_list;
}
return ($resource) ? $resource->fetch_all(MYSQLI_ASSOC) : [];
}
//affected rows from last query
public function get_affected_rows(){
return $this->connection->affected_rows;
}
public function getErrorNo(){
return mysqli_errno($this->connection);
}
public function __destruct() {
$this->disconnect();
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Hura8\Database;
class MysqlValue
{
protected $value;
public function __construct($value = null) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Hura8\Database;
interface iConnectDB
{
public function isConnected(): bool;
public function ping() ;
/**
* @param string $query
* @param array $bind_types
* @param array $bind_values
* @param bool $get_affected_row_or_id get the affected row or newly insert-id from the query, default return the mysqli_result
* @return \mysqli_result | false | int
*/
public function runQuery($query, array $bind_types=[], array $bind_values=[], $get_affected_row_or_id = false);
/**
* @deprecated Unsafe, make sure all variables are properly escaped
*/
public function multi_query(array $array_query);
// a simple utitily method which we use very frequently
public function getItemInfo($table_name, $id_value, $id_field = 'id');
//$table_name = 'table_abc'
/*$fields = array(
"field_name_1",
);*/
/*$where_condition = array(
"field_name_1" => ["=", "value_1"],
"field_name_2" => ["LIKE", "value_1"],
);*/
public function select($table_name, $fields =[], $where_condition=[], $order_by = '', $limit='1');
/**
* @param \mysqli_result | false $resource
* @return array|null
*/
public function fetchAssoc( $resource);
/**
* @param \mysqli_result | false $resource
* @return array|mixed
*/
public function fetchAll($resource);
public function getErrorNo();
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* Copyright (c) 2021. www.Hura.vn
*/
namespace Hura8\Interfaces;
class APIResponse
{
private $errCode = 0;
private $msg = '';
private $data;
public function __construct($errCode = 0, $msg = '', $data = null) {
$this->errCode = $errCode;
$this->msg = $msg;
$this->data = $data;
}
/**
* @return int
*/
public function getCode() {
return $this->errCode;
}
/**
* @return mixed if status = 'error'
*/
public function getMsg() {
return $this->msg;
}
/**
* @return mixed if status = 'ok'
*/
public function getData() {
return $this->data;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Hura8\Interfaces;
class AppResponse
{
const SUCCESS = 'ok';
const ERROR = 'error';
protected $status;
protected $msg = null;
protected $data;
public function __construct($status = 'ok', $msg = null, $data = null) {
$this->status = $status;
$this->msg = $msg;
$this->data = $data;
}
/**
* @return string which is 'ok' or 'error'
*/
public function getStatus() {
return $this->status;
}
/**
* @return mixed if status = 'error'
*/
public function getMsg() {
return $this->msg;
}
/**
* @return mixed if status = 'ok'
*/
public function getData() {
return $this->data;
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace Hura8\Interfaces;
/**
* Contain all entity types of this apps
*
* Dynamic Resolution Rules:
* - Format: entity_type = [parent_type]-[sub_type]
* Examples:
* + product: Product Entity
* + product-category: [Product parent-Entity]-[Product Category sub-Entity]
* + user-comment-reply: [user parent-Entity]-[user comment sub-Entity]-[user comment reply sub-Entity]
*
* - Model Resolution: EntityModel is auto inferred from entity name: EntityModel = Hura8\Components\[parent_type]\Model\[entity-name-upper first char]Model
* Examples:
* + product: Hura8\Components\Product\Model\ProductModel
* + product-category: Hura8\Components\Product\Model\ProductCategoryModel
* + user-comment: Hura8\Components\User\Model\UserCommentModel
* + user-comment-reply: Hura8\Components\User\Model\UserCommentReplyModel
*
* - Controller Resolution: similar to the Model
* Examples: public controllers
* + product: Hura8\Components\Product\PublicController\UProductController
* + product-category: Hura8\Components\Product\PublicController\UProductCategoryController
* + user-comment: Hura8\Components\User\PublicController\UUserCommentController
* + user-comment-reply: Hura8\Components\User\PublicController\UUserCommentReplyController
*
* Examples: admin controllers
* + product: Hura8\Components\Product\AdminController\AProductController
* + product-category: Hura8\Components\Product\AdminController\AProductCategoryController
* + user-comment: Hura8\Components\User\AdminController\AUserCommentController
* + user-comment-reply: Hura8\Components\User\AdminController\AUserCommentReplyController
*
*/
final class EntityType
{
/**
* - Model Resolution: EntityModel is auto inferred from entity name: EntityModel = Hura8\Components\[parent_type]\Model\[entity-name-upper first char]Model
* Examples:
* + product: Hura8\Components\Product\Model\ProductModel
* + product-category: Hura8\Components\Product\Model\ProductCategoryModel
* + user-comment: Hura8\Components\User\Model\UserCommentModel
* + user-comment-reply: Hura8\Components\User\Model\UserCommentReplyModel
*/
public static function getModelClass(string $entity_type): string {
$parts = array_map(function ($word){
return ucfirst($word);
}, explode("-", $entity_type));
$parent = $parts[0];
return "\\Hura8\\Components\\".$parent."\\Model\\".join("", $parts)."Model";
}
public static function getStatisticModelClass(string $entity_type): string {
$parts = array_map(function ($word){
return ucfirst($word);
}, explode("-", $entity_type));
$parent = $parts[0];
return "\\Hura8\\Components\\".$parent."\\Model\\".join("", $parts)."StatisticModel";
}
/*
* - Controller Resolution: similar to the Model
* Examples: public controllers
* + product: Hura8\Components\Product\PublicController\UProductController
* + product-category: Hura8\Components\Product\PublicController\UProductCategoryController
* + user-comment: Hura8\Components\User\PublicController\UUserCommentController
* + user-comment-reply: Hura8\Components\User\PublicController\UUserCommentReplyController
* */
public static function getControllerClass(string $entity_type, $controller_type='public'): string {
$parts = array_map(function ($word){
return ucfirst($word);
}, explode("-", $entity_type));
$parent = $parts[0];
// Hura8\Components\Product\PublicController\UProductCategoryController
if($controller_type == 'public') {
return "\\Hura8\\Components\\".$parent."\\PublicController\\U".join("", $parts)."Controller";
}
// Hura8\Components\Product\AdminController\AProductCategoryController
return "\\Hura8\\Components\\".$parent."\\AdminController\\A".join("", $parts)."Controller";
}
// system's entities
const PRODUCT = 'product';
const PRODUCT_CATEGORY = 'product-category';
const PRODUCT_COLLECTION = 'collection';
const PRODUCT_ATTRIBUTE = 'attribute';
const PRODUCT_ATTRIBUTE_VALUE = 'attribute-value';
const COUPON = 'coupon';
const ORDER = 'order';
const ARTICLE = 'article';
const ARTICLE_CATEGORY = 'article-category';
const BANNER = 'banner';
const BANNER_LOCATION = 'banner-location';
const CUSTOMER = 'customer'; // registered customers
const USER = 'user'; // all users, include customers
const USER_CONTACT = 'user-contact';
const USER_COMMENT = 'user-comment';
const USER_COMMENT_REPLY = 'user-comment-reply';
const BRAND = 'brand';
const DEAL = 'deal';
const MEDIA = 'media';
const TAG = 'tag';
const URL = 'url';
const COMBO_SET = 'combo-set';
const PAGE = 'page';
const PAGE_CATEGORY = 'page-category';
const ALBUM = 'album';
const ALBUM_CATEGORY = 'album-category';
const ALBUM_PHOTO = 'album-photo';
const PROVINCE = 'province';
const PROMOTION = 'promotion';
const PROMOTION_GROUP = 'promotion-group';
const DISTRIBUTOR = 'distributor';
const WARRANTY = 'warranty';
const STAFF = 'staff'; // admin web
const VIDEO = 'video';
const VIDEO_CATEGORY = 'video-category';
const JOB = 'job';
const JOB_CATEGORY = 'job-category';
const DOMAIN = 'domain';
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Hura8\Interfaces;
class FileHandleInfo
{
public $file_name;
public $public_path;
public $local_path;
public $mime_type;
public $file_size;
public $file_ext;
public $width = 0;
public $height = 0;
public function __construct(array $file_info = []) {
/*$file_info = [
"file_name" => $clean_file_name,
"public_path" => $this->public_dir . "/".$clean_file_name,
"local_path" => $this->target_dir . "/" . $clean_file_name,
"mime_type" => $mimeType,
"file_size" => $file_size,
"file_ext" => $file_ext,
"width" => 0,
"height" => 0,
];*/
$this->file_name = $file_info['file_name'] ?? '' ;
$this->public_path = $file_info['public_path'] ?? '' ;
$this->local_path = $file_info['local_path'] ?? '' ;
$this->mime_type = $file_info['mime_type'] ?? '' ;
$this->file_size = $file_info['file_size'] ?? 0 ;
$this->file_ext = $file_info['file_ext'] ?? '' ;
$this->width = $file_info['width'] ?? 0 ;
$this->height = $file_info['height'] ?? 0 ;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Hura8\Interfaces;
class FileHandleResponse extends AppResponse
{
public function __construct($status = 'ok', $msg = null, ?FileHandleInfo $data = null) {
parent::__construct($status, $msg, $data);
}
/**
* @return ?FileHandleInfo
*/
public function getData() : ?FileHandleInfo {
return parent::getData();
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Hura8\Interfaces;
class PermissionRole
{
const OWNER = 'owner';
const ADMIN = 'admin';
const EDITOR = 'editor';
const VIEWER = 'viewer';
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Hura8\Interfaces;
class PermissionType
{
const VIEW = 'view';
const CREATE = 'create';
const DELETE = 'delete';
const UPDATE = 'update';
const APPROVE = 'approve';
const LOCK = 'lock';
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Hura8\Interfaces;
final class TableName
{
const COLLECT_FORM = 'tb_collect_form';
const COLLECT_FORM_FILTER = 'tb_collect_form_filter';
const TEMPLATE = "tb_template";
const TEMPLATE_SET = 'tb_template_set';
const TEMPLATE_HISTORY = "tb_template_history";
const MEDIA = 'tb_media_upload';
const COMMENT = "tb_user_comment";
const COMMENT_REPLY = "tb_user_comment_reply";
const PRODUCT = 'tb_product';
const PRODUCT_INFO = 'tb_product_info';
const PRODUCT_CATEGORY = 'tb_product_category';
const PRODUCT_CATEGORY_INFO = 'tb_product_category_info';
const PRODUCT_PER_CATEGORY = 'tb_product_per_category';
const PRODUCT_VARIANT = 'tb_product_variant';
const PRODUCT_IMAGE_NAME = 'tb_product_image';
const PRODUCT_IMAGE_STOCK = 'tb_product_image_stock';
const PRODUCT_ACCESSORY = 'tb_product_accessory';
const PRODUCT_HOT = 'tb_product_hot';
const PRODUCT_COLLECTION = 'tb_collection';
const PRODUCT_PER_COLLECTION = 'tb_collection_product';
const PRODUCT_FILTER = 'tb_product_filter';
const PRODUCT_PER_ATTRIBUTE = 'tb_product_attribute';
const ATTRIBUTE = 'tb_attribute';
const ATTRIBUTE_INFO = 'tb_attribute_info';
const ATTRIBUTE_CATEGORY = 'tb_attribute_category';
const ATTRIBUTE_VALUE = 'tb_attribute_value';
const ARTICLE = 'tb_article';
const ARTICLE_COMMENT = 'tb_article_comment';
const ARTICLE_CATEGORY = 'tb_article_category';
const ARTICLE_INFO = 'tb_article_info';
const ARTICLE_PER_CATEGORY = 'tb_article_per_category';
const ARTICLE_IMAGE = 'tb_article_image';
const BRAND = 'tb_brand';
const COUPON = 'tb_coupon';
const COUPON_USE = 'tb_coupon_use';
const DEAL = 'tb_deal';
const DEAL_CONTENT = 'tb_deal_content';
const COMBO_DEAL = 'tb_combo_deal';
const COMBO_DEAL_DETAIL = 'tb_combo_deal_detail';
const ORDER = 'tb_order';
const ORDER_ITEM = 'tb_order_detail_new';
const CUSTOMER_POINT = "tb_customer_point";
const CUSTOMER = "tb_customer";
const CUSTOMER_GROUP = "tb_customer_group";
const CUSTOMER_PER_GROUP = 'tb_customer_group_list';
const CUSTOMER_ADDRESS = "tb_customer_address";
const CUSTOMER_UPLOAD = 'tb_user_upload';
const CUSTOMER_LIKE = 'tb_user_like';
const CUSTOMER_PREFERENCE = "tb_user_preference";
const CUSTOMER_CONTACT = "tb_customer_contact";
const WEB_USER = "tb_web_user_info";
const PAYGATE = 'tb_paygate';
const PAY_METHOD = 'tb_pay_method';
const SHIP_METHOD = 'tb_shipping_method';
const REPORT_VISIT_SUMMARY = 'tb_visit_summary';
const REPORT_ERROR_PAGE = 'tb_report_error_page';
const REPORT_REFERER_DOMAIN = 'tb_visit_referer_domain';
const REPORT_REFERER_SUMMARY = 'tb_visit_referer_summary';
const PROVINCE = "tb_province_list";
const PROVINCE_DISTRICT = "tb_province_district_list";
const PROVINCE_WARD = "tb_province_ward_list";
const DOMAIN = 'tb_domain';
const URL = 'tb_url';
const URL_META = 'tb_url_meta';
const STAFF = "tb_staff";
const DEPARTMENT = 'tb_department';
const STAFF_LOG = "tb_staff_log";
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* Created by Glee Ltd.
* Description: interface to work with our clients
*/
namespace Hura8\Interfaces;
interface iClientERP
{
public function createOrder(array $order_info) ;
/**
* get log data
*/
public function getLog($type, $limit = 50);
/**
* log data
*/
public function log($type, array $data);
/**
* @description: clean any existing data before populate new ones
*/
public function cleanExistingData();
// get summary of products in the system
public function getProductSummary();
// save product from erp to tmp tables
public function saveProductToWeb(array $erp_product_list);
// start sync tmp tables to actual tables
public function syncProductToWeb(array $options = []);
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Hura8\Interfaces;
interface iCustomUrlBuilder
{
public function productUrl($product_id, array $input = []);
public function productCategoryUrl($category_id, array $input = []);
public function articleUrl($article_id, array $input = []);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Hura8\Interfaces;
/**
* This interface must be implemented by all erp providers as in package/provider/Provider/ERPProviders
*/
interface iERPProvider
{
public function createOrder(array $order_info);
public function getProductList(array $options = [], $debug=false);
public function getProductSummary(array $options = [], $debug=false);
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 14-Jul-19
* Time: 3:21 PM
* Description:
*/
namespace Hura8\Interfaces;
interface iEmail
{
// setup
public function setUp(array $config);
// send email
public function send(array $to_emails, $subject, $content);
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Hura8\Interfaces;
interface iEntityAdminCategoryController extends iEntityCategoryController, iEntityAdminController
{
public function getDropBox($selectedId, $categoryParentId, $level=1);
public function categorySelectBox(array $array_selected, $categoryParentId, $level=1);
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Hura8\Interfaces;
interface iEntityAdminController extends iEntityController
{
public function create(array $info) : AppResponse;
public function update($id, array $info) : AppResponse;
public function delete($id) : AppResponse;
public function getEmptyInfo(array $additional_fields = []): array;
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Hura8\Interfaces;
interface iEntityCategoryController extends iEntityController
{
public function getAllParent(array $condition = []);
public function getNestedCategories($is_public = false);
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Hura8\Interfaces;
interface iEntityCategoryModel extends iEntityModel
{
public function getAllByParent(array $condition = array()): array;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Hura8\Interfaces;
interface iEntityController
{
public function getListByIds(array $list_id, array $condition = array()): array;
/**
* @description utility to inspect the actual filters which will be used in getList by Controller
* @param array $raw_filter_condition
* @return string[]
*/
public function getActualFilterCondition(array $raw_filter_condition) : array;
/**
* @description utility to inspect the actual filters which will be used in getList by Model
* @param array $raw_filter_condition
* @return string[]
*/
public function getModelFilterCondition(array $raw_filter_condition) : array;
public function getList(array $condition): array;
public function getTotal(array $condition): int;
public function getInfo($id): ?array;
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Hura8\Interfaces;
interface iEntityLanguageModel
{
// create a necessary language table to hold the translated data for the language
public function createTableLang(): AppResponse;
public function setLanguage(string $language): bool;
// any fields to have language: title, summary, description, price, etc...
public function setLanguageFields(array $language_fields);
public function getLanguageFields() : array ;
public function getEntityType() : string ;
public function update($id, array $new_input_info, $search_keyword = "") : AppResponse;
public function deleteAll($id): AppResponse;
public function delete($id): AppResponse;
public function getListByIds(array $list_id): array;
// get list of ids which are in the language table. This is to filter which records have been translated and which not in the main table
// this is limited to 50k items. Which means for item_type having more than 50k records, this checking method is not suitable
public function getTranslatedIds() : array;
public function getInfo($id): ?array;
public function getTotal(array $condition): int;
// get empty/default item for form
public function getEmptyInfo(): array;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Hura8\Interfaces;
interface iEntityModel
{
public function getEntityType() : string ;
public function getListByIds(array $list_id, array $condition = array()) : array;
public function getList(array $condition) : array;
public function getTotal(array $condition) : int;
public function getQueryCondition(array $condition) : array;
public function getInfo($id): ?array;
public function getEmptyInfo(array $additional_fields = []): array;
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Hura8\Interfaces;
interface iEntityPermission
{
public function canCreate(): bool;
public function canUpdate(): bool;
public function canDelete(): bool;
public function canView(): bool;
public function canApprove(): bool;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Hura8\Interfaces;
interface iEntityStatistic
{
// get data
public function getListComment(array $condition): array;
public function getTotalComment(array $condition) : int;
public function getInfoComment($id): ?array;
public function getListReview(array $condition): array;
public function getTotalReview(array $condition) : int;
public function getInfoReview($id): ?array;
// update from public
public function updateCommentCount($id, int $total=0, int $avg_rate=0): AppResponse;
public function updateReviewCount($id, int $total=0, int $avg_rate=0): AppResponse;
public function updateVisitCount($id, int $total): AppResponse;
public function updateLikeCount($id, int $total): AppResponse;
public function updateSaveCount($id, int $total): AppResponse;
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 25-Apr-19
* Time: 11:16 AM
* Description:
*/
namespace Hura8\Interfaces;
interface iExcelDownload
{
/**
* @param array $options
* @return mixed
*/
public function start(array $options);
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 19-Jun-19
* Time: 1:29 PM
* Description:
*/
namespace Hura8\Interfaces;
interface iPayGate
{
// create an pay url to redirect users from website to the payment gateway's site
public function createPayUrl(array $config);
// process the result return by paygate
//public function processPayResult();
/**
* create payid to sent to vnpay and also track in the system. One order can be paid by multiple installments
*
* @param $item_type string 'order', 'wait-order',
* @param $item_id int id of order
* @param $expected_amount int the amount to pay in this payment
* @param $description string
* @param $user_info array
* @return int
*/
public function createPayId($item_type, $item_id, $expected_amount, $description, array $user_info);
// process the webhook sent by paygate's server
public function processWebhook();
// get list of supported banks
//public function getBankList();
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 18-Jan-19
* Time: 11:03 AM
* Description:
*/
namespace Hura8\Interfaces;
use Hura8\User\UProduct;
interface iPricing
{
// apply for a list of items and return modified list
public function applyForList(array $item_list, UProduct $objUProduct);
// apply for a item and return modified item
public function applyForItem(array $item_info, UProduct $objUProduct);
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 28-Jun-19
* Time: 3:26 PM
* Description:
*/
namespace Hura8\Interfaces;
interface iProductPromotionProgram
{
public function activate($id, $status);
public function updateSetting($id, array $settings);
public function updateProductSetting($program_id, $product_id, array $settings);
public function addProduct($program_id, $product_id, array $settings);
public function removeProduct($program_id, $product_id);
public function getProductList($program_id);
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Hura8\Interfaces;
interface iPublicEntityController
{
// get data
public function getListByIds(array $list_id, array $condition = array()): array;
public function getList(array $condition): array;
public function getTotal(array $condition) : int;
public function getInfo($id): ?array;
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 10-Oct-18
* Time: 11:24 AM
* Description:
*/
namespace Hura8\Interfaces;
interface iSMS
{
public function send($mobile, $content, $debug = false);
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Hura8\Interfaces;
interface iSearch
{
/**
* @description get filter fields
* @param array[string]
*/
public function getFilterFields(): array;
/**
* @description get fulltext fields
* @param array[string]
*/
public function getFulltextFields(): array;
/**
* @description update or create item if not exist
* @param $item_id
* @param array $table_field_values
$table_field_values = [
"tb_product.price" => 2000000,
"tb_product.title" => "Máy tính ABC",
"tb_category.price" => "Máy tính",
];
* @return bool
*/
public function updateItem($item_id, array $table_field_values = []): AppResponse;
/**
* @description delete item from index
* @param $item_id
*/
public function deleteItem($item_id): AppResponse;
/**
* @description delete items from index
* @param $item_list_ids
*/
public function deleteItems(array $item_list_ids): AppResponse;
/**
* @param string $keyword
* @param array $field_filters
* @param array $fulltext_fields
* @param int $limit_result
* @return array[int]
*/
public function find(string $keyword, array $field_filters = [], array $fulltext_fields = [], int $limit_result = 2000) : array;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Hura8\Interfaces;
interface iShippingCost extends iShippingProvider
{
// get only options for a cart
public function getShippingOptionsForCart(array $cart_item_list, $province, $district, $ward=0) : array;
// return array [shipping_fee, cod_fee]
public function calculateShippingCostForOrder($selected_shipping_option, array $shipping_address, array $order_info) ;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Hura8\Interfaces;
interface iShippingProvider
{
// get all available shipping options, which might or might not be suitable for an order
public function getAllShippingOptions(array $args) : array;
// get only options for a specific order (based on order's value, shipping address ...) as suggested by the shipping provider
public function getShippingOptions(array $args) : array;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Hura8\Interfaces;
interface iSyncWebErpProduct
{
public function erpToWebProductVariant();
public function erpToWebProduct();
public function webToErpProduct();
public function getErpTotalProduct();
}

60
inc/Hura8/Router.php Normal file
View File

@@ -0,0 +1,60 @@
<?php
namespace Hura8;
class Router {
private $path_config = [];
public function __construct() {
//if( ! $this->path_config) {
//$path_config_file = APP_DIR . '/config/routing.php';
//$this->path_config = require $path_config_file;
//}
}
// url: admin/abc/product.php?para1=value1
public function getRouting() {
$parsed = Url::parse($_SERVER['REQUEST_URI']); //abc/product?param1=12&param2=value2
// home
if($parsed['path'] == '/') {
return [
'module' => preg_replace("/[^a-z0-9_\-]/i","", getRequest('module', 'home')),
'view' => preg_replace("/[^a-z0-9_\-]/i","", getRequest('view', 'home')),
'view_id' => 0,
'query' => $parsed['query'],
];
}
// check match pattern in $this->path_config
foreach ($this->path_config as $_config => $_route ) {
if(preg_match("{^".$_config."$}", $parsed['path'], $match )) {
if(isset($_route['query']) && is_array($_route['query'])) {
$_route['query'] = array_merge($_route['query'], $parsed['query']);
}else{
$_route['query'] = $parsed['query'];
}
return array_merge([
'path' => $parsed['path'],
'match' => $match,
], $_route);
}
}
// auto parse path base on convention: admin/module/view/view_id
$ele = explode("/", $parsed['path']);
// else error
return [
'module' => $ele[2] ?? 'home',
'view' => isset($ele[3]) ? preg_replace("/[^a-z0-9_\-]/i","", $ele[3] ) : 'home',
'view_id' => isset($ele[4]) ? preg_replace("/[^a-z0-9_]/i","", $ele[4] ) : '',
'query' => $parsed['query'],
];
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* Created by Glee Ltd.
* User: Hieu
* Date: 22-Apr-19
* Time: 11:18 AM
* Description:
*/
namespace Hura8\System;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
final class APIClient
{
private $headers = [];
/* @var $client \GuzzleHttp\Client */
protected $client;
public function __construct($endpoint, $timeout = 10)
{
$this->client = new Client([
// Base URI is used with relative requests
'base_uri' => $endpoint,
// You can set any number of default request options.
'timeout' => $timeout,
]);
}
public function post($path, array $parameters) {
return $this->call_service("post", $path, $parameters);
}
public function get($path, array $parameters) {
return $this->call_service("get", $path, $parameters);
}
public function setHeaders(array $headers) {
// Authorization:
/*$headers = [
'Authorization' => "Basic ". base64_encode($this->api_key),
];*/
$this->headers = $headers;
}
//ref: http://docs.guzzlephp.org/en/stable/quickstart.html#query-string-parameters
// make a consistent call with the same $payload format: ["key" => "value", ...]
/*
return json {
"errCode" => 1|0, (1=error, 0=success)
"msg" => "error_message" | "",
}*/
protected function call_service($http_method, $path, array $payload) {
try {
// get
if( $http_method == 'get' ) {
$response = $this->client->request('GET', $path, [
'headers' => $this->headers,
'query' => $payload
]);
}
// post
else {
$request_options = $this->buildPostRequestOptions($this->headers, $payload);
$response = $this->client->request('POST', $path, $request_options);
}
return [
"errCode" => "0",
"msg" => $response->getBody()->getContents(),
];
}
catch (ServerException $e) {
$response = $e->getResponse();
//$errors = \json_decode($response->getBody()->getContents(), true);
return [
"errCode" => 1,
"msg" => $response->getBody()->getContents(),
];
}
catch (ClientException $e) {
$response = $e->getResponse();
//$errors = \json_decode($response->getBody()->getContents(), true);
return [
"errCode" => 2,
"msg" => $response->getBody()->getContents(),
];
}
catch (\Exception $e) {
return [
"errCode" => 3,
"msg" => "Exception: ".$e->getMessage(),
];
}
}
// ref: http://docs.guzzlephp.org/en/stable/request-options.html
// form_params cannot be used with the multipart option. You will need to use one or the other.
// Use form_params for application/x-www-form-urlencoded requests, and multipart for multipart/form-data requests.
protected function buildPostRequestOptions(array $headers, array $payload) {
$content_type = isset($headers["Content-Type"]) ? $headers["Content-Type"] : "";
if($content_type == "application/x-www-form-urlencoded") {
return [
'headers' => $headers,
'form_params' => $payload,
//'debug' => true
];
}
if($content_type == "application/json") {
return [
'headers' => $headers,
'json' => $payload,
//'body' => json_encode($payload),
];
}
// reformat the payload for multipart
$multipart_request = [];
foreach ($payload as $key => $value) {
if( ! $key) continue;
$multipart_request[] = [
'name' => $key,
'contents' => (is_array($value)) ? json_encode($value) : $value,
];
}
// multipart/form-data
return [
'headers' => $headers,
'multipart' => $multipart_request,
];
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* Created by Hurasoft.
* Date: 28-May-2022
* Description: Use this class to send local files to the cdn host (endpoint: cdn.host/file_upload_handle.php). The cdn host also has a copy of Hura8 main class to receive the uploaded files
*/
namespace Hura8\System;
class CDNFileUpload {
//protected $upload_handle = 'http://local.hura8/file_upload_handle.php';
protected $upload_handle = STATIC_DOMAIN . '/file_upload_handle.php';
protected $user_id = 0;
protected $target_path_item_type_mapping = [
'product' => 'media/product', // media/product
'article' => 'media/news', // media/news
'brand' => 'media/brand',
'category' => 'media/category',
'banner' => 'media/banner',
'lib' => 'media/lib/',
'user_upload' => 'media/user_upload',
];
public function __construct($user_id) {
$this->user_id = $user_id;
}
/**
* @description: an implementation of start method to upload an item's image
* @param $item_type string
* @param $content string
* @param $file_name string
* @return array | boolean
*/
public function uploadFile($item_type, $file_name, $content, $set_target_path = '')
{
//$file_name = substr(strrchr($img_url, "/"), 1);
//$content = get_url_content($img_url);
$header_setting = [
// 'Authorization' => "Basic ". base64_encode(API_KEY),
];
if($set_target_path) {
$target_path = $set_target_path;
}else{
$target_path = (isset($this->target_path_item_type_mapping[$item_type])) ? $this->target_path_item_type_mapping[$item_type] : '';
}
$multipart_settings = [
[
'name' => 'upload_method',
'contents' => 'content',
],
[
'name' => 'file_name',
'contents' => $file_name,
],
[
'name' => 'target_path',
'contents' => $target_path,
],
[
'name' => 'file_content',
'contents' => $content,
],
//for upload server
[
'name' => 'uid',
'contents' => $this->user_id,
],
[
'name' => 'time',
'contents' => CURRENT_TIME,
],
[
'name' => 'token',
'contents' => $this->createToken($this->user_id, CURRENT_TIME),
],
[
'name' => 'item_type',
'contents' => $item_type ,
],
];
return $this->start($header_setting, $multipart_settings);
}
/*
let settings = {
element : '',//element id to click on
holder : '', //id file holder
form_name : '',
item_type : '',
item_id : '',
file_type : '', //what is the file type: doc, photo, video ?
file_field : '', //what field in the item table this file is used for ?
resize : '200,400,600' //sizes to resize
square: '200,400', //sizes to be square
keep_width : '400', //sizes to keep the width
max_file: 1
}
*/
/**
* @description: a general method for upload
* @param array $header_setting
* @param array $multipart_settings
* @return array | boolean
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function start(array $header_setting = [], array $multipart_settings = []) {
$client = new \GuzzleHttp\Client();
$request = $client->request('POST', $this->upload_handle, [
'headers' => $header_setting,
'multipart' => $multipart_settings
]);
return $request->getBody()->getContents();
}
// return string | boolean
protected function getAssetType($file_name) {
$ext = strtolower(strrchr($file_name, "."));
if( $ext == '.css') return 'css';
if( $ext == '.js') return 'js';
if( \in_array($ext, ['.jpg', '.jpeg', '.gif', '.png', '.ico'])) return 'image';
return false;
}
// this salt and createToken must be the same ones as in Hura8/Base/CDNFileUploadHandle
// and different per project (or else others will use it to upload forbidden files on our clients' hosting)
const SECURITY_SALT = 'ahss@3asdaaSDFSD';
protected function createToken($id, $time) {
return sha1(join("-", [$id , $time , static::SECURITY_SALT]));
}
}

View File

@@ -0,0 +1,275 @@
<?php
/**
* Created by Hurasoft
* Date: 28-May-2022
* Time: 1:44 PM
* Description: Use this class in the cdn host to handle the upload of files from Hura8's admin and put the uploaded file in the cdn host's local directory
*/
namespace Hura8\System;
class CDNFileUploadHandle
{
const SECURITY_SALT = 'ahss@3asdaaSDFSD';
//default config
protected $config = [
//1. file settings
"max_file_size" => 2000000,//bytes ~ 1MB
'allowed_file_types' => [
'image' => ['.jpeg', '.jpg', '.gif', '.png'],
'script' => ['.css', '.js'],
],
//2. uploaded by client
'uid' => '',
'token' => '',
'time' => '',
'item_type' => 'product', // product|article|media
'target_path' => '',
'upload_method' => 'content', //file || content
"file_content" => "", //only needed if upload_method = content
"file_name" => "",//only needed if upload_method = content
];
private $tmp_file_prop = null; //array, temporary file's props in tmp folder
private $accepted_file_input_names = [
"file", //<input type=file name="file">
//"qqfile", //name use for files uploaded through FineUploader library
];
private $user_tmp_dir = '';
public function __construct(){
}
protected function createToken($id, $time) {
return sha1(join("-", [$id , $time , static::SECURITY_SALT]));
}
public function start() {
$this->_get_post_info();
if(!$this->validateUser()) {
return $this->set_return_result('error', 'User failed to verify', []);
}
//receive files
$file = $this->receive_file();
//return file-location to upload API to return to client application
return $this->set_return_result('success', 'Upload succeeded', $file );
}
protected function validateUser() {
return ($this->config['token'] == $this->createToken($this->config['uid'], $this->config['time']));
}
protected function _get_post_info() {
$expected_keys = [
'upload_method',
'file_name',
'target_path',
'file_content',
'uid',
'time',
'token',
'item_type',
];
foreach ($expected_keys as $key) {
$this->config[$key] = isset($_POST[$key]) ? $_POST[$key] : null;
}
}
//return boolean
protected function remove_tmp_file() {
$this->deleteDirectory($this->user_tmp_dir);
return true;
}
//return boolean
protected function validate_file() {
//$this->tmp_file_prop
//validate file size
if($this->config['max_file_size'] && $this->config['max_file_size'] < $this->tmp_file_prop['size']) {
return 'Size too large';
}
//validate allowed extension: allow image but upload .docx files
$has_extension = false;
$file_type = '';
foreach ( $this->config['allowed_file_types'] as $group => $group_ext ) {
if(in_array( $this->tmp_file_prop['ext'], $group_ext )) {
$has_extension = true;
$file_type = $group;
break ;
}
}
if( ! $has_extension ) return "File type not allowed";
//validate claimed extension: claim image but not actual image
$full_file_path = $this->tmp_file_prop['tmp_location'] . DIRECTORY_SEPARATOR . $this->tmp_file_prop['name'];
if( $file_type == 'image' && ! v::image()->validate( $full_file_path )) {
return "Not actual image";
}
return '';
}
//return mixed : array $tmp_file_prop or false
protected function receive_file() {
//check upload method
if( $this->config['upload_method'] == 'content' ) {
//upload by content - file created on server
$file_ext = $this->get_ext($this->config['file_name']);
$upload_folder = $this->create_upload_folder() ;
if( ! $this->move_file($upload_folder)) {
return false;
}
return [
"ext" => $file_ext,
"name" => $this->config['file_name'],
"folder" => $upload_folder,
"size" => strlen($this->config['file_content']),
];
} else {
//upload by file
//get list of files to be uploaded
$file_uploaded = null;
foreach ($this->accepted_file_input_names as $_name) {
if(isset($_FILES[$_name])) {
$file_uploaded = $_FILES[$_name];
break;
}
}
if ( ! $file_uploaded ) return false;
//if file name too long, then error
if ( strlen($file_uploaded['name']) > 225 ) return false;
//move file to tmp folder
$file_ext = $this->get_ext($file_uploaded['name']);
$upload_folder = $this->create_upload_folder() ;
if( ! $this->move_file($upload_folder) ){
return false;
}
return [
"ext" => $file_ext,
"name" => $this->config['file_name'],
"folder" => $upload_folder,
"size" => $file_uploaded['size'],
];
}
}
protected function set_return_result($status = 'success', $message = 'Upload success', $content = []) {
return [
"status" => $status,
"message" => $message,
"files" => $content,
];
}
//create upload folder for user: use current date, user id or app id based on $this->config
private function create_upload_folder() {
//$build_folder = [$this->tmp_dir];
/*$user_id = (isset($this->config['user_id'])) ? intval($this->config['user_id']) : null;
if($user_id) {
$build_folder[] = $user_id;
}
$build_folder[] = date("Ymd");*/
//rebuild upload folder
$build_folder = array_filter(explode("/", $this->config['target_path']));
$folder = join("/", $build_folder);
$this->createDir($folder);
return $folder;
}
//get a file's extension
private function get_ext($fileName){
return strtolower(strrchr($fileName, '.'));
}
// check if find is image
private function isImage($fileName) {
return (in_array( $this->get_ext($fileName), ['.jpeg', '.jpg', '.gif', '.png'] ));
}
//for security reason: only allow a-z0-9_- in file name (no other dot . except for the extension)
//example: bad-file.php.js -> bad-filephp.js
private function rename_file($uploaded_file_name, $ext) {
$new_name = preg_replace("/[^a-z0-9_\-]/i", "", str_replace( strrchr($uploaded_file_name, '.'), "", $uploaded_file_name ) ) . $ext;
return strtolower($new_name);
}
private function move_file($folder) {
$new_file = $folder . '/' . $this->config['file_name'];
if( $this->config['upload_method'] == 'content' ) {
// image upload by content
if( $this->isImage($new_file) ) {
//Store in the filesystem.
$fp = fopen($new_file, "w+");
$status = fwrite($fp, $this->config['file_content']);
fclose($fp);
return ($status !== false);
}
// file upload
else if( file_put_contents($new_file, $this->config['file_content'])){
return true;
}
}
return false;
}
protected function createDir($path, $folder_permission = 0750){
if(file_exists($path)) {
return true;
}
return mkdir($path, $folder_permission, true);
}
protected function deleteDirectory($dirPath) {
if (!is_dir($dirPath)) {
return false;
}
$objects = scandir($dirPath);
foreach ($objects as $object) {
if ($object != "." && $object !="..") {
if (filetype($dirPath . DIRECTORY_SEPARATOR . $object) == "dir") {
$this->deleteDirectory($dirPath . DIRECTORY_SEPARATOR . $object);
} else {
unlink($dirPath . DIRECTORY_SEPARATOR . $object);
}
}
}
return rmdir($dirPath);
}
}

100
inc/Hura8/System/Cache.php Normal file
View File

@@ -0,0 +1,100 @@
<?php
namespace Hura8\System;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
class Cache
{
private static $redisCache = null;
private static $fileCache = null;
protected function __construct() { }
public static function getFileInstance(): FilesystemAdapter {
if(static::$fileCache instanceof FilesystemAdapter) {
return static::$fileCache;
}
static::$fileCache = new FilesystemAdapter(
// a string used as the subdirectory of the root cache directory, where cache
// items will be stored
$namespace = '',
// the default lifetime (in seconds) for cache items that do not define their
// own lifetime, with a value 0 causing items to be stored indefinitely (i.e.
// until the files are deleted)
$defaultLifetime = 30,
// the main cache directory (the application needs read-write permissions on it)
// if none is specified, a directory is created inside the system temporary directory
$directory = CACHE_FILE_SYSTEM_DIRECTORY
);
return static::$fileCache;
}
/**
* @return RedisAdapter | false
*/
public static function getRedisInstance() {
if(!is_null(static::$redisCache)) {
return static::$redisCache;
}
try {
// pass a single DSN string to register a single server with the client
$redisConnection = RedisAdapter::createConnection( CACHE_REDIS_DSN , [
//'read_timeout' => 10,
//'retry_interval' => 2,
/*
'persistent' => 1,
'persistent_id' => null,
'timeout' => 10,
'tcp_keepalive' => 0,
'lazy' => null,
'redis_cluster' => false,
'redis_sentinel' => null,*/
] );
//var_dump($redisConnection->isConnected());
}catch (\Exception $exception) {
//echo $exception->getMessage();
$redisConnection = false;
}
if(!$redisConnection) {
static::$redisCache = false;
return false;
}
static::$redisCache = new RedisAdapter(
// the object that stores a valid connection to your Redis system
$redisConnection,
// the string prefixed to the keys of the items stored in this cache
$namespace = '',
// the default lifetime (in seconds) for cache items that do not define their
// own lifetime, with a value 0 causing items to be stored indefinitely (i.e.
// until RedisAdapter::clear() is invoked or the server(s) are purged)
$defaultLifetime = 10
);
return static::$redisCache;
}
}

208
inc/Hura8/System/Config.php Normal file
View File

@@ -0,0 +1,208 @@
<?php
namespace Hura8\System;
use Hura8\Traits\ClassCacheTrait;
class Config
{
use ClassCacheTrait;
public static function getRequestLanguage() : string {
return static::getCache("getRequestLanguage", function () {
$lang = $_REQUEST[Constant::LANGUAGE_ID] ?? DEFAULT_LANGUAGE;
return (array_key_exists($lang , Constant::languagePermitList())) ? $lang : DEFAULT_LANGUAGE;
});
}
public static function getClientInfo() {
return static::getCache("getClientInfo", function () {
$config_file = CONFIG_DIR . '/client/client.info.php';
if(file_exists($config_file)) {
return include $config_file;
}
return [];
});
}
public static function getProductHotTypeList(){
return static::getCache('getProductHotTypeList', function (){
$config_file = CONFIG_DIR . "/client/product.hottype.php";
if(!file_exists($config_file)) {
die("File: client/product.hottype.php not exist !");
}
return include $config_file;
});
}
public static function getRoutes() {
return static::getCache("getRoutes", function () {
$config_file = CONFIG_DIR . '/client/routing_main.php';
if(!file_exists($config_file)) {
die("File not exist: config/client/routing_main.php");
}
return include $config_file;
});
}
public static function getEntityLanguageFields() {
return static::getCache("getEntityLanguageFields", function () {
$config_file = CONFIG_DIR . '/client/language_fields.php';
if(!file_exists($config_file)) {
die("File not exist: config/client/language_fields.php");
}
return include $config_file;
});
}
public static function getSettingsEmail() {
return static::getCache("getSettingsEmail", function () {
$system_config_file = CONFIG_DIR . '/system/settings.email.php';
if(!file_exists($system_config_file)) {
die("File not exist: config/system/settings.email.php");
}
$system_config = include $system_config_file;
// client extension
$client_config_file = CONFIG_DIR . '/client/settings.email-extend.php';
$client_config = [];
if(file_exists($client_config_file)) {
$client_config = include $client_config_file;
}
$final_config = $system_config;
if(isset($client_config['email'])) {
$final_config['email'] = array_merge($final_config['email'], $client_config['email']);
}
if(isset($client_config['template'])) {
$final_config['template'] = array_merge($final_config['template'], $client_config['template']);
}
return $final_config;
});
}
public static function getSettingsPrint() {
return static::getCache("getSettingsPrint", function () {
$config_file = CONFIG_DIR . '/system/settings.print.php';
if (!file_exists($config_file)) {
// die("File not exist: config/system/settings.print.php");
return [];
}
return include $config_file;
});
}
public static function getRelationConfigForItem($item_type) {
return static::getCache("getRelationConfigForItem-".$item_type, function () use ($item_type) {
$config_relation_file = ROOT_DIR . '/config/client/config_relation.php';
if(!file_exists($config_relation_file)) {
die("config/client/config_relation.php does not exist!");
}
$system_relation_file = ROOT_DIR . '/config/system/relation_config.php';
if(!file_exists($system_relation_file)) {
die("config/system/relation_config.php does not exist!");
}
$config_relation = include $config_relation_file;
$available_content = include $system_relation_file;
if(isset($config_relation[$item_type])) {
$config = array();
foreach ($config_relation[$item_type] as $type) {
if(isset($available_content[$type])) $config[$type] = $available_content[$type];
}
return $config;
}
return [];
});
}
public static function getProductImageTypes(){
$config_image_type_file = ROOT_DIR . "/config/client/config_product_image_types.php";
if(!file_exists($config_image_type_file)){
return [
'standard' => 'Hình sản phẩm',
];
}
return include $config_image_type_file;
}
public static function getProductUnitList() {
return static::getCache("getProductUnitList", function (){
$config_file = CONFIG_DIR . "/client/product.unit.php";
if(file_exists($config_file)) {
return include $config_file;
}
return [];
});
}
public static function getLanguageCount(){
$language_list = static::getLanguageConfig();
$count = sizeof($language_list);
return ($count > 1) ? $count : 1; //always have at least 1 language
}
public static function getLanguageConfig(){
return static::getCache('getLanguageConfig', function (){
$config_file = CONFIG_DIR . "/client/language_enable.php";
if(!file_exists($config_file)) {
return [];
}
return include $config_file;
});
}
public static function getClientBuildConfig() {
return static::getCache("getClientBuildConfig", function (){
$config_file = CONFIG_DIR . '/build/store.config.php';
if(file_exists($config_file)) {
return include $config_file;
}
return [];
});
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Hura8\System;
use Hura8\Traits\ClassCacheTrait;
use ReflectionClass;
class Constant
{
use ClassCacheTrait;
private static $constant_dir = CONFIG_DIR . '/system';
const LANGUAGE_ID = "_l"; // to identify the language setting on URL or in FORM
public static function getAllEntityTypes() {
return static::getCache('getAllEntityTypes', function () {
$extend_type_config = CONFIG_DIR . DIRECTORY_SEPARATOR . "/client/config_extend_entity_type.php";
$extended_types = [];
if(file_exists($extend_type_config)) {
$extend_types = include_once $extend_type_config;
foreach ($extend_types as $key => $title) {
$extended_types[] = preg_replace("/[^a-z0-9_\-]/i", "", $key);
}
}
$list = [];
try {
$reflectionClass = new \ReflectionClass(new \Hura8\Interfaces\EntityType());
foreach ($reflectionClass->getConstants() as $handle => $value) {
$list[] = $value;
}
// add extend
foreach ($extended_types as $type) {
if(!in_array($type, $list)) {
$list[] = $type;
}
}
} catch (\ReflectionException $e) {
//
}
return $list;
});
}
// list of languages can be enabled
public static function languagePermitList() {
return [
"vi" => "Tiếng Việt", // default
"en" => "English",
];
}
public static function mobileProviderPrefixList() {
return static::getCache('mobileProviderPrefixList', function () {
return include static::$constant_dir. '/mobile_provider.php';
});
}
public static function customerStageList() {
return static::getCache('customerStageList', function () {
return include static::$constant_dir. '/customer_stage_list.php';
});
}
public static function saluteList() {
return static::getCache('genderList', function () {
return include static::$constant_dir. '/salute_list.php';
});
}
public static function genderList() {
return static::getCache('genderList', function () {
return include static::$constant_dir. '/gender_list.php';
});
}
public static function industryList() {
return static::getCache('industryList', function () {
return include static::$constant_dir. '/industry_list.php';
});
}
public static function maritalStatusList() {
return static::getCache('maritalStatusList', function () {
return include static::$constant_dir. '/marital_status_list.php';
});
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace Hura8\System\Controller;
use Hura8\System\Model\DomainModel;
class DomainController
{
protected $objDomainModel;
public function __construct()
{
$this->objDomainModel = new DomainModel();
}
protected $layout_options = [
"pc" => "Chỉ cho PC",
"mobile" => "Chỉ cho Mobile",
//"amp" => "Chỉ cho AMP",
"all" => "Cả PC & Mobile",
];
public function buildDomainConfig() {
$domain_per_languages = $this->getList('');
$config_domain_list = []; //all domains and attributes, so we can know the info of currently visited domain
$config_domain_languages = []; //domains per language, so we can redirect to main domain of a specific language
foreach ($domain_per_languages as $lang => $list_domains) {
foreach ($list_domains as $_item) {
$config_domain_languages[$lang][] = [
"domain" => $_item['domain'],
"is_main" => $_item['isMain'],
"layout" => ($_item['layout']) ? $_item['layout'] : 'pc',
];
$config_domain_list[$_item['domain']] = [
"lang" => $_item['lang'],
"is_main" => $_item['isMain'],
"layout" => ($_item['layout']) ? $_item['layout'] : 'pc',
];
}
}
return [
"language" => $config_domain_languages,
"list" => $config_domain_list,
];
}
public function getLayoutOption(){
return $this->layout_options;
}
public function getList($language = '') {
$item_list = $this->objDomainModel->getList([
"language" => $language,
"numPerPage" => 100,
]);
$result = array();
foreach ( $item_list as $rs ) {
if(!$rs['lang']) $rs['lang'] = DEFAULT_LANGUAGE;
$result[$rs['lang']][] = $rs;
}
return $result;
}
public function addNewDomain($domain, $language = DEFAULT_LANGUAGE){
$this->objDomainModel->addNewDomain($this->cleanDomain($domain), $language);
$this->rebuildConfigFile();
}
public function deleteDomain($domain){
$this->objDomainModel->deleteDomain($this->cleanDomain($domain));
$this->rebuildConfigFile();
}
public function setDomainMain($domain, $language){
$this->objDomainModel->setDomainMain($this->cleanDomain($domain), $language);
$this->rebuildConfigFile();
}
public function setDomainLayout($domain, $layout = 'pc'){
$layout_option = $this->getLayoutOption();
if(!isset($layout_option[$layout])) {
return false;
}
$this->objDomainModel->setDomainLayout($this->cleanDomain($domain), $layout);
$this->rebuildConfigFile();
return true;
}
protected function cleanDomain($domain) {
$domain_element = parse_url($domain);
$scheme = isset($domain_element['scheme']) ? $domain_element['scheme'] . '://' : '';
$host = $domain_element['host'] ?? '';
$port = isset($domain_element['port']) ? ':' . $domain_element['port'] : '';
return strtolower(trim($scheme . $host . $port));
}
protected function rebuildConfigFile() {
$objSettingController = new SettingController();
$objSettingController->create_config_file_n_upload();
}
}

Some files were not shown because too many files have changed in this diff Show More