This commit is contained in:
2025-10-04 11:46:59 +07:00
commit 97427d7cff
498 changed files with 47596 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
<?php
namespace Hura8\Components\Analytics\Controller;
use Hura8\Components\Analytics\Model\TrackingModel;
class bTrackingController
{
protected $objTrackingModel;
public function __construct()
{
$this->objTrackingModel = new TrackingModel();
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Hura8\Components\Analytics\Model;
class TrackDeviceInfo
{
public $ip_address;
public $user_agent;
public $referrer;
public $is_mobile;
public function __construct(string $ip_address, string $user_agent, string $referrer, bool $is_mobile)
{
$this->ip_address = $ip_address;
$this->user_agent = $user_agent;
$this->referrer = $referrer;
$this->is_mobile = $is_mobile;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Hura8\Components\Analytics\Model;
class TrackRouteInfo
{
public $url;
public $module;
public $view;
public $view_id;
public $query;
public function __construct(
string $url,
string $module,
string $view,
string $view_id,
array $query = []
)
{
$this->url = $url;
$this->module = $module;
$this->view = $view;
$this->view_id = $view_id;
$this->query = $query;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Hura8\Components\Analytics\Model;
class TrackUserInfo
{
public $web_user_id;
public $customer_id;
public $is_crawler;
public function __construct(string $web_user_id, string $customer_id, bool $is_crawler)
{
$this->web_user_id = $web_user_id;
$this->customer_id = $customer_id;
$this->is_crawler = $is_crawler ? 1 : 0;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Hura8\Components\Analytics\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iEntityModel;
use Hura8\System\Model\aEntityBaseModel;
class TrackingModel extends aEntityBaseModel implements iEntityModel
{
protected $tb_track_ip = "tb_analyics_track_ip";
public function __construct() {
parent::__construct(
"analyics_user_log"
);
}
protected function extendedFilterOptions(): array
{
// TODO: Implement extendedFilterOptions() method.
}
protected function _buildQueryConditionExtend(array $filter_condition): ?array
{
// TODO: Implement _buildQueryConditionExtend() method.
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Hura8\Components\Article\AdminController;
use Hura8\Components\Article\Controller\bArticleCategoryController;
use Hura8\Interfaces\iEntityAdminCategoryController;
use Hura8\Traits\AdminEntityCategoryControllerTraits;
class AArticleCategoryController extends bArticleCategoryController implements iEntityAdminCategoryController
{
use AdminEntityCategoryControllerTraits;
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Hura8\Components\Article\AdminController;
use Hura8\Components\Article\Controller\bArticleController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class AArticleController extends bArticleController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function updateTableInfo($item_id, array $new_item_info) {
if(!$this->isDefaultLanguage()) {
return $this->iEntityLanguageModel->update($item_id, $new_item_info);
}
return $this->objArticleModel->updateTableInfo($item_id, $new_item_info);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Hura8\Components\Article\Controller;
use Hura8\Components\Article\Model\ArticleCategoryLanguageModel;
use Hura8\Components\Article\Model\ArticleCategoryModel;
use Hura8\System\Controller\aCategoryBaseController;
class bArticleCategoryController extends aCategoryBaseController
{
/* @var ArticleCategoryModel $objArticleCategoryModel */
protected $objArticleCategoryModel;
public function __construct()
{
$this->objArticleCategoryModel = new ArticleCategoryModel();
if(!$this->isDefaultLanguage()) {
parent::__construct(
$this->objArticleCategoryModel,
new ArticleCategoryLanguageModel()
);
} else {
parent::__construct($this->objArticleCategoryModel);
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Hura8\Components\Article\Controller;
use Hura8\Components\Article\Model\ArticleLanguageModel;
use Hura8\Components\Article\Model\ArticleModel;
use Hura8\System\Controller\aEntityBaseController;
class bArticleController extends aEntityBaseController
{
static $image_folder = "media/article";
static $resized_sizes = array(
't' => ['width' => 200,] ,
'l' => ['width' => 600,] ,
);
/* @var ArticleModel $objArticleModel */
protected $objArticleModel;
public function __construct()
{
$this->objArticleModel = new ArticleModel();
if(!$this->isDefaultLanguage()) {
parent::__construct(
$this->objArticleModel,
new ArticleLanguageModel()
);
} else {
parent::__construct($this->objArticleModel);
}
}
public function getFullInfo($id)
{
if(!$id) return null;
return self::getCache("getFullInfo-".$id."-".$this->view_language, function () use ($id){
$info = $this->objArticleModel->getFullInfo($id);
if($this->iEntityLanguageModel && $info ) {
$item_language_info = $this->iEntityLanguageModel->getInfo($id) ?? ["not_translated" => true];
return $this->formatItemInfo(array_merge($info, $item_language_info));
}
return ($info) ? $this->formatItemInfo($info) : null;
});
}
protected function formatItemInList(array $item_info)
{
return $this->formatItemInfo($item_info);
}
protected function formatItemInfo(array $item_info)
{
if(!$item_info) return null;
$info = $item_info;
$info['image'] = self::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 : '';
}
return $image;
}
}

View File

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

View File

@@ -0,0 +1,33 @@
<?php
namespace Hura8\Components\Article\Model;
use Hura8\System\Model\aCategoryBaseModel;
use Hura8\Interfaces\iEntityCategoryModel;
use Hura8\Interfaces\EntityType;
class ArticleCategoryModel extends aCategoryBaseModel implements iEntityCategoryModel
{
static $url_module = "article";
static $url_view = "category";
static $url_type = "article:category";
protected $tb_article_per_category = "tb_article_per_category";
public function __construct() {
parent::__construct(EntityType::ARTICLE_CATEGORY);
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
return null;
}
}

View File

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

View File

@@ -0,0 +1,150 @@
<?php
namespace Hura8\Components\Article\Model;
use Hura8\System\Controller\UrlManagerController;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\ModuleManager;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
class ArticleModel extends aEntityBaseModel implements iEntityModel
{
static $url_type = "article:detail";
protected $tb_article_info = "tb_article_info";
protected $tb_article_per_category = 'tb_article_per_category';
public function __construct() {
parent::__construct(
EntityType::ARTICLE,
"",
new ArticleSearchModel()
);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getFullInfo($id) : ?array
{
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` basic, `".$this->tb_article_info."` info
WHERE basic.`id` = info.`article_id` AND basic.id = ?
LIMIT 1 ",
['d'], [$id]
);
if( $item_info = $this->db->fetchAssoc($query)){
return $item_info;
}
return null;
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"category" => getRequestInt("category"),
"no_image" => 0,//1
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
//Tim danh muc
if(isset($filter_condition["category"]) && $filter_condition["category"]) {
$objArticleCategoryModel = new ArticleCategoryModel();
$category_info = $objArticleCategoryModel->getInfo($filter_condition["category"]);
if($category_info) {
if($category_info['is_parent']) {
$catCondition[] = " AND `id` IN (SELECT `item_id` FROM `".$this->tb_article_per_category."` WHERE `category_id` IN (".$category_info['child_ids'].") ) ";
//$bind_types[] = 'd';
//$bind_values[] = $filter_condition["category"];
}else{
$catCondition[] = " AND `id` IN (SELECT `item_id` FROM `".$this->tb_article_per_category."` WHERE `category_id` = ? ) ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition["category"];
}
}
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
protected function addArticleToCategory($item_id, array $category_list_id) {
$this->db->runQuery("DELETE FROM `".$this->tb_article_per_category."` WHERE `item_id` = ? ", ['d'], [$item_id]);
$bulk_inserts = [];
foreach($category_list_id as $cat_id) {
if (! $cat_id) continue;
$bulk_inserts[] = [
'category_id' => $cat_id,
'item_id' => $item_id,
'status' => 1,
'create_time' => CURRENT_TIME,
];
}
if(sizeof($bulk_inserts)) {
$this->db->bulk_insert($this->tb_article_per_category, $bulk_inserts);
}
// update counter
$objArticleCategoryModel = new ArticleCategoryModel();
foreach($category_list_id as $cat_id) {
$objArticleCategoryModel->updateItemCount($cat_id);
}
}
public function updateUrl($id, $url_index): bool
{
$module_routing = ModuleManager::getModuleRouting("article");
$request_path_config = isset($module_routing["detail"]) ? $module_routing["detail"]['url_manager']['request_path'] : '';
if(!$request_path_config) {
return false;
}
$request_path = UrlManagerController::translateRequestPathConfig($request_path_config, $id, $url_index);
$id_path = UrlManagerController::createIdPath("article", "detail", $id);
$objUrlManager = new UrlManagerController();
$new_request_path = $objUrlManager->createUrl("article:detail", $request_path, $id_path, 0);
if($new_request_path) {
$this->db->update(
$this->tb_entity,
[
'request_path' => $new_request_path,
],
[
'id' => $id,
]
);
}
return true;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Hura8\Components\Article\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class ArticleSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
"status"=> "tb_article.status",
];
private $fulltext_fields = [
"keywords" => ["tb_article.title", ],
];
public function __construct()
{
parent::__construct(
"tb_article",
$this->fulltext_fields,
$this->filter_fields
);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Hura8\Components\Article\Model;
class UArticleModel extends ArticleModel
{
public function getSameCategoryArticle($main_id, $category_id){
$query = $this->db->runQuery("
(
SELECT `item_id`
FROM ".$this->tb_article_per_category."
WHERE `category_id` = ? AND `status`=1 AND `item_id` > ?
ORDER BY `item_id` DESC
LIMIT 10
) UNION ALL (
SELECT `item_id`
FROM ".$this->tb_article_per_category."
WHERE `category_id` = ? AND `status`=1 AND `item_id` < ?
ORDER BY `item_id` DESC
LIMIT 10
)
",
['d', 'd', 'd', 'd'],
[$category_id, $main_id, $category_id, $main_id]
);
$article_list_id = [];
$article_item_info = array();
$article_item = [];
foreach ( $this->db->fetchAll($query) as $rs ) {
if(!isset($article_item_info[$rs["item_id"]])) $article_item_info[$rs["item_id"]] = array();
if(!in_array($rs["item_id"], $article_list_id)) $article_list_id[] = $rs["item_id"];
if($rs["item_id"] > $main_id) {
$article_item['new'][$rs["item_id"]] = &$article_item_info[$rs["item_id"]];
}
else {
$article_item['old'][$rs["item_id"]] = &$article_item_info[$rs["item_id"]];
}
}
$list_article_info = $this->getListByIds($article_list_id);
foreach ($article_list_id as $_id) {
if(isset($list_article_info[$_id])) $article_item_info[$_id] = $list_article_info[$_id];
}
return $article_item;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Hura8\Components\Banner\AdminController;
use Hura8\Components\Banner\Controller\bBannerController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class ABannerController extends bBannerController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Hura8\Components\Banner\AdminController;
use Hura8\Components\Banner\Model\BannerLocationModel;
use Hura8\System\Controller\aAdminEntityBaseController;
class ABannerLocationController extends aAdminEntityBaseController
{
public function __construct()
{
parent::__construct(new BannerLocationModel());
}
public function getTemplateBanner() {
return ABannerController::$template_banners;
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Hura8\Components\Banner\Controller;
use Hura8\Components\Banner\Model\BannerLocationModel;
use Hura8\Components\Banner\Model\BannerModel;
use Hura8\System\Controller\aEntityBaseController;
class bBannerController extends aEntityBaseController
{
static $image_folder = "media/banner";
static $template_banners = array(
//"index" => "Toàn bộ website" ,
"header" => "Đầu trang" ,
"homepage" => "Trang chủ" ,
"column_left" => "Cột trái" ,
"column_right" => "Cột phải" ,
"footer" => "Chân trang" ,
"product_detail"=> "Chi tiết sản phẩm" ,
"product_list" => "Danh sách &amp; Danh mục sản phẩm" ,
"collection_list" => "Bộ sưu tập" ,
"article_home" => "Trang chủ tin tức" ,
"brand_detail" => "Chi tiết thương hiệu",
);
protected $objBannerModel;
protected $objBannerLocationModel;
public function __construct()
{
$this->objBannerModel = new BannerModel();
$this->objBannerLocationModel = new BannerLocationModel();
parent::__construct($this->objBannerModel);
}
protected function formatItemInList(array $item_info)
{
return self::formatFile($item_info);
}
protected function formatItemInfo(array $item_info)
{
return self::formatFile($item_info);
}
public static function formatFile(array $item_info)
{
if($item_info['file_url']) {
$item_info['display_file'] = STATIC_DOMAIN ."/". static::$image_folder ."/". $item_info['file_url'];
}else if($item_info['file_external_url']) {
$item_info['display_file'] = $item_info['file_external_url'];
}
$item_info['html_code'] = "<a href=\"/ad.php?id=".$item_info['tracking_id']."\" target='_blank' rel='nofollow'><img border='0' src=\"".$item_info['display_file']."\" alt=\"".htmlspecialchars($item_info['title'])."\" /></a>";
return $item_info;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Hura8\Components\Banner\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
class BannerLocationModel extends aEntityBaseModel implements iEntityModel
{
public function __construct() {
parent::__construct(EntityType::BANNER_LOCATION);
}
protected function extendedFilterOptions(): array
{
return [];
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
return null;
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Hura8\Components\Banner\Model;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
class BannerModel extends aEntityBaseModel implements iEntityModel
{
protected $tb_banner_location = "tb_banner_location";
protected $tb_banner_per_category = "tb_banner_per_category";
public function __construct() {
parent::__construct(
EntityType::BANNER, "", new BannerSearchModel()
);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getInfoByTrackingId($tracking_id)
{
$query = $this->db->runQuery("SELECT * FROM `".$this->tb_entity."` WHERE `tracking_id` = ? LIMIT 1 ", ['s'], [$tracking_id]) ;
if( $item_info = $this->db->fetchAssoc($query)){
return $this->formatItemInfo($item_info);
}
return false;
}
public function getBannerPerTemplate(array $template_list, $numberOfBannerPerTpl=100){
$all_bind_types = [];
$all_bind_values = [];
$view_id = 0;
$build_query = [];
foreach($template_list as $tpl) {
list($where_condition, $bind_types, $bind_values) = $this->buildQueryPerTpl($tpl, $view_id, $numberOfBannerPerTpl);
$build_query[] = " (".$where_condition.") ";
$all_bind_types = array_merge($all_bind_types, $bind_types);
$all_bind_values = array_merge($all_bind_values, $bind_values);
}
if(!sizeof($build_query)) return [];
$query = $this->db->runQuery(join(" UNION ALL ", $build_query), $all_bind_types, $all_bind_values);
return $this->db->fetchAll($query);
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
[location] => 2
[category] => 0
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
if(isset($filter_condition['location']) && $filter_condition['location']) {
$catCondition[] = " AND `location` = ? ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition['location'];
}
if(isset($filter_condition['category']) && $filter_condition['category']) {
$catCondition[] = " AND `id` IN ( SELECT `banner_id` FROM `".$this->tb_banner_per_category."` WHERE `category_id` = ? ) ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition['category'];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Hura8\Components\Banner\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class BannerSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
"location" => "tb_banner.location",
"status" => "tb_banner.status",
];
private $fulltext_fields = [
"keywords" => ["tb_banner.title",],
];
public function __construct()
{
parent::__construct(
"tb_banner",
$this->fulltext_fields,
$this->filter_fields
);
//$this->createTableSearch();
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Hura8\Components\Brand\AdminController;
use Hura8\Components\Brand\Controller\bBrandController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class ABrandController extends bBrandController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function getGroupByFirstLetter() {
return $this->objBrandModel->getGroupByFirstLetter();
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// delete thumb files
$item_info = $this->getInfo($item_id);
if($item_info['thumbnail']) {
foreach (static::$resized_sizes as $size => $value) {
$file_local_path = PUBLIC_DIR . "/". static::$image_folder . "/". $size. IMAGE_FILE_SEPARATOR . $item_info['thumbnail'];
unlink($file_local_path);
}
// remove original file
$file_local_path = PUBLIC_DIR . "/". static::$image_folder . "/". $item_info['thumbnail'];
unlink($file_local_path);
}
//delete media files?
// todo:
// ok
return true;
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Hura8\Components\Brand\Controller;
use Hura8\Components\Brand\Model\BrandLanguageModel;
use Hura8\Components\Brand\Model\BrandModel;
use Hura8\System\Controller\aEntityBaseController;
class bBrandController extends aEntityBaseController
{
static $image_folder = "media/brand";
static $resized_sizes = array(
's' => ['width' => 200,] ,
);
/* @var BrandModel $objBrandModel */
protected $objBrandModel;
/* @var BrandLanguageModel $objBrandLanguageModel */
protected $objBrandLanguageModel;
protected $view_language = '';
public function __construct()
{
$this->objBrandModel = new BrandModel();
if(!$this->isDefaultLanguage()) {
$this->objBrandLanguageModel = new BrandLanguageModel();
//$this->objVideoLanguageModel->createTableLang();
parent::__construct($this->objBrandModel, $this->objBrandLanguageModel);
}else{
parent::__construct($this->objBrandModel);
}
}
public function getInfoByUrl(string $band_index) : ?array
{
return $this->objBrandModel->getInfoByUrl($band_index);
}
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 = static::formatItemImage($item_info);
$info['url'] = "/brand/".$info['brand_index'];
return $info;
}
public static function formatItemImage(array $item_info) {
$info = $item_info;
foreach (static::$resized_sizes as $size => $value) {
$info['image'][$size] = ($info['thumbnail']) ? STATIC_DOMAIN . "/". static::$image_folder . "/". $size. IMAGE_FILE_SEPARATOR . $info['thumbnail'] : '';
}
return $info;
}
}

View File

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

View File

@@ -0,0 +1,72 @@
<?php
namespace Hura8\Components\Brand\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Config;
use Hura8\System\Controller\UrlManagerController;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
use Hura8\Interfaces\TableName;
class BrandModel extends aEntityBaseModel implements iEntityModel
{
static $url_type = "brand:detail";
public function __construct() {
parent::__construct(EntityType::BRAND);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getGroupByFirstLetter() {
$query = $this->db->runQuery(
"SELECT `letter`, COUNT(*) AS item_count FROM `".$this->tb_entity."` GROUP BY `letter` ORDER BY `letter` ASC "
);
return $this->db->fetchAll($query);
}
protected function _buildQueryConditionExtend(array $filter_condition): ?array
{
/*$condition = array(
"letter" => "",
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
if(isset($filter_condition["letter"]) && strlen($filter_condition["letter"]) == 1){
$catCondition[] = " AND `letter` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["letter"];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
public function getInfoByUrl($brand_index) : ?array
{
$brand_index = preg_replace("/[^a-z0-9\.\-\_]/i", '', $brand_index);
$query = $this->db->runQuery("SELECT * FROM `".$this->tb_entity."` WHERE `brand_index` = ? LIMIT 1 ", ['s'], [$brand_index]);
if($item_info = $this->db->fetchAssoc($query)){
return $this->formatItemInfo($item_info);
}
return null;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Hura8\Components\ComboSet\AdminController;
use Hura8\Components\ComboSet\Controller\bComboSetController;
use Hura8\Components\Product\AdminController\AProductController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class AComboSetController extends bComboSetController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Hura8\Components\ComboSet\Controller;
use Hura8\Components\ComboSet\Model\ComboSetLanguageModel;
use Hura8\Components\ComboSet\Model\ComboSetModel;
use Hura8\System\Controller\aEntityBaseController;
class bComboSetController extends aEntityBaseController
{
/* @var ComboSetModel $objComboSetModel */
protected $objComboSetModel;
/* @var ComboSetLanguageModel $objComboSetLanguageModel */
protected $objComboSetLanguageModel;
public function __construct()
{
$this->objComboSetModel = new ComboSetModel();
if(!$this->isDefaultLanguage()) {
$this->objComboSetLanguageModel = new ComboSetLanguageModel();
//$this->objVideoLanguageModel->createTableLang();
parent::__construct($this->objComboSetModel, $this->objComboSetLanguageModel);
}else{
parent::__construct($this->objComboSetModel);
}
}
public function getAllSetIdsForAProduct($product_id)
{
return $this->objComboSetModel->getAllSetIdsForAProduct($product_id);
}
public function getTotalProductUseSet($set_id)
{
return $this->objComboSetModel->getTotalProductUseSet($set_id);
}
public function getListProductUseSet($set_id, $numPerPage)
{
return $this->objComboSetModel->getListProductUseSet($set_id, $numPerPage);
}
public function getProductListInfoInConfig(array $category) {
$product_list_ids = [];
foreach ($category as $index => $_category_info) {
foreach ($_category_info['suggest_list'] as $_proindex => $_pro_info) {
$product_list_ids[] = $_pro_info['real_id'];
}
}
return array_unique($product_list_ids);
}
public function buildConfig( $category, $product) {
$group_category = [];
foreach ($category as $category_index => $_category_info) {
$category_product = [];
foreach ($product[$category_index] as $product_index => $_product_info) {
//$_product_info['price'] = clean_price($_product_info['price']);
$category_product[] = $_product_info;
}
$group_category[] = [
"title" => $_category_info['title'],
//"type" => "category",
//"real_id" => $_category_info['real_id'],
//"select_type" => $_category_info['select_type'],//checkbox|radio
"suggest_list" => $category_product,
];
}
return $group_category;
}
public function decomposeConfig($config) {
$tab = [];
$group = [];
$category = [];
$product = [];
$group_index = 0;
$category_index = 0;
$product_index = 0;
foreach ($config as $tab_index => $tab_info) {
//construct tab
$tab[$tab_index] = [
'title' => $tab_info['title'],
];
//construct group
foreach ($tab_info['child'] as $child_group) {
$group_index += 1;
$group[$tab_index][$group_index] = [
'title' => $child_group['title'],
];
//construct category
foreach ($child_group['child'] as $child_category) {
$category_index += 1;
$category[$group_index][$category_index] = [
'title' => $child_category['title'],
'real_id' => $child_category['real_id'],
'select_type' => $child_category['select_type'],
];
//construct product
foreach ($child_category['suggest_list'] as $child_product) {
$product_index += 1;
$product[$category_index][$product_index] = [
'title' => $child_product['title'],
'real_id' => $child_product['real_id'],
'is_default' => $child_product['is_default'],
];
}
}
}
}
return [
"tab" => $tab,
"group" => $group,
'category' => $category,
'product' => $product,
];
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Hura8\Components\ComboSet\Model;
use Hura8\Interfaces\EntityType;
use Hura8\System\Model\EntityLanguageModel;
class ComboSetLanguageModel extends EntityLanguageModel
{
public function __construct() {
parent::__construct('combo_set');
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace Hura8\Components\ComboSet\Model;
use Hura8\Components\Product\AdminController\AProductController;
use Hura8\Components\Product\Model\ProductSearchModel;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iEntityModel;
use Hura8\System\Model\aEntityBaseModel;
class ComboSetModel extends aEntityBaseModel implements iEntityModel
{
protected $tb_set_product = 'tb_combo_set_product';
public function __construct() {
parent::__construct('combo_set');
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getAllSetIdsForAProduct($product_id)
{
$query = $this->db->runQuery(
" SELECT `set_id` FROM ".$this->tb_set_product." WHERE `product_id` = ? ",
['d'], [$product_id]
);
$item_list = array();
foreach ( $this->db->fetchAll($query) as $info ) {
$item_list[] = $info['set_id'];
}
return $item_list;
}
public function getTotalProductUseSet($set_id)
{
// search
$keyword = getRequest("q");
if($keyword) {
$search = new ProductSearchModel();
$match_result = $search->find($keyword);
$catCondition = (sizeof($match_result) > 0) ? " AND `product_id` IN (".join(",", $match_result).") " : " AND `product_id` = -1 ";
$query = $this->db->runQuery("
SELECT COUNT(product_id) AS total_product
FROM ".$this->tb_set_product."
WHERE `set_id` = ? " . $catCondition ."
", ['d'], [$set_id]);
if ($info = $this->db->fetchAssoc($query)) {
return $info['total_product'];
}
return 0;
} else {
$set_info = $this->getInfo($set_id);
return $set_info['product_count'];
}
}
public function getListProductUseSet($set_id, $numPerPage)
{
$page = getPageId();
// search
$catCondition = "";
$keyword = getRequest("q");
if($keyword) {
$search = new ProductSearchModel();
$match_result = $search->find($keyword);
$catCondition = (sizeof($match_result) > 0) ? " AND `product_id` IN (".join(",", $match_result).") " : " AND `product_id` = -1 ";
}
$query = $this->db->runQuery("
SELECT `product_id`
FROM ".$this->tb_set_product."
WHERE `set_id` = ? " . $catCondition ."
ORDER BY id desc
LIMIT ".($page - 1) * $numPerPage .", ".$numPerPage."
", ['d'], [$set_id]);
$item_list = array();
foreach ( $this->db->fetchAll($query) as $info ) {
$item_list[] = $info['product_id'];
}
return $item_list;
}
protected function _buildQueryOrderBy(string $sort_by = "new")
{
$order_condition = "";
switch ($sort_by) {
case "ordering";
$order_condition = " `ordering` desc ";
break;
case "old";
$order_condition = " id asc ";
break;
case "last_show_time";
$order_condition = " last_show_time ASC ";
break;
}
return $order_condition;
}
protected function formatItemInfo(array $item_info) : array
{
$from_time = $item_info['from_time'];
$from_time_date = ($from_time > 0) ? date("d-m-Y", $from_time) : '';
$from_time_minute = ($from_time > 0) ? date("H:i", $from_time) : "00:00";
$to_time = $item_info['to_time'];
$to_time_date = ($to_time > 0) ? date("d-m-Y", $to_time) : '';
$to_time_minute = ($to_time > 0) ? date("H:i", $to_time) : "00:00";
$item_info['from_time_date'] = $from_time_date;
$item_info['from_time_minute'] = $from_time_minute;
$item_info['to_time_date'] = $to_time_date;
$item_info['to_time_minute'] = $to_time_minute;
return $item_info;
}
///---------
///
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$catCondition = "";
$bind_types = [];
$bind_values = [];
if(isset($filter_condition["product_id"]) && $filter_condition["product_id"]){
$catCondition .= " AND `id` IN ( SELECT `set_id` FROM ".$this->tb_set_product." WHERE `product_id` = ? ) ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition['product_id'];
}
return [$catCondition, $bind_types, $bind_values];
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Hura8\Components\ConfigGroup\AdminController;
use Hura8\Components\ConfigGroup\Controller\bConfigGroupController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class AConfigGroupController extends bConfigGroupController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function deleteAttribute($id, $group_id = 0) {
$this->objConfigGroupModel->deleteAttribute($id, $group_id);
}
public function updateAttribute($id, $info) {
$this->objConfigGroupModel->updateAttribute($id, $info) ;
}
public function createAttribute($info) {
return $this->objConfigGroupModel->createAttribute($info) ;
}
public function createAttributeValue($info) {
$this->objConfigGroupModel->createAttributeValue($info);
}
public function deleteAttributeValue($id) {
$this->objConfigGroupModel->deleteAttributeValue($id);
}
public function updateAttributeValue($id, $info) {
$this->objConfigGroupModel->updateAttributeValue($id, $info);
}
public function createProduct($product_id, $group_id, array $attribute_config) {
$this->objConfigGroupModel->createProduct($product_id, $group_id, $attribute_config);
}
public function deleteProduct($product_id, $group_id) {
$this->objConfigGroupModel->deleteProduct($product_id, $group_id);
}
public function updateProduct($product_id, $group_id, array $attribute_config, $product_name_in_group = '') {
return $this->objConfigGroupModel->updateProduct($product_id, $group_id, $attribute_config, $product_name_in_group);
}
public function getProductInGroup($group_id){
return $this->objConfigGroupModel->getProductInGroup($group_id);
}
public function getGroupConfig($item_id) {
return $this->objConfigGroupModel->getGroupConfig($item_id);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Hura8\Components\ConfigGroup\Controller;
use Hura8\Components\ConfigGroup\Model\ConfigGroupModel;
use Hura8\System\Controller\aEntityBaseController;
class bConfigGroupController extends aEntityBaseController
{
/* @var ConfigGroupModel $objConfigGroupModel */
protected $objConfigGroupModel;
public function __construct()
{
$this->objConfigGroupModel = new ConfigGroupModel();
parent::__construct($this->objConfigGroupModel);
}
}

View File

@@ -0,0 +1,500 @@
<?php
namespace Hura8\Components\ConfigGroup\Model;
use Hura8\Components\Product\Model\ProductModel;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
class ConfigGroupModel extends aEntityBaseModel
{
protected $tb_config_group = "";
protected $tb_config_group_product = "tb_config_group_product";
protected $tb_config_group_product_cache = "tb_config_group_product_cache";
protected $tb_config_group_attribute = "tb_config_group_attribute";
protected $tb_config_group_attribute_value = "tb_config_group_attribute_value";
public function __construct() {
parent::__construct('config_group');
$this->tb_config_group = $this->tb_entity;
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
protected function updateGroupAttributeCount($group_id) {
$this->db->runQuery(
"UPDATE `".$this->tb_config_group."` SET
`attribute_count` = ( SELECT COUNT(*) FROM `".$this->tb_config_group_attribute."` WHERE `group_id` = ? )
WHERE `id` = ? LIMIT 1 ",
['d', 'd'], [ $group_id, $group_id ]
);
}
protected function updateGroupProductCount($group_id) {
$this->db->runQuery(
"UPDATE `".$this->tb_config_group."` SET
`item_count` = ( SELECT COUNT(*) FROM `".$this->tb_config_group_product."` WHERE `group_id` = ? )
WHERE `id` = ? LIMIT 1 ",
['d', 'd'], [ $group_id, $group_id ]
);
}
//attribute value
public function deleteAttributeValue($att_value_id) {
$attr_id = 0;
$query = $this->db->runQuery("SELECT `attr_id` FROM `".$this->tb_config_group_attribute_value."` WHERE `id` = ? LIMIT 1 ", ['d'], [ $att_value_id ]);
if ($info = $this->db->fetchAssoc($query)) {
$attr_id = $info['attr_id'];
}
$group_id = $this->getGroupIdFromAttribute($attr_id);
$this->db->runQuery("DELETE FROM `".$this->tb_config_group_attribute_value."` WHERE `id` = ? LIMIT 1 ", ['d'], [ $att_value_id ]);
$this->resetProductConfigCache($group_id);
$this->updateAttributeValueCount($attr_id);
}
protected function updateAttributeValueCount($attr_id) {
$this->db->runQuery(
"UPDATE `".$this->tb_config_group_attribute."` SET
`value_count` = ( SELECT COUNT(*) FROM `".$this->tb_config_group_attribute_value."` WHERE `attr_id` = ? )
WHERE `id` = ? LIMIT 1 ",
['d', 'd'],
[$attr_id, $attr_id ]
);
}
public function updateAttributeValue($id, $info) {
$updated_info = $info;
$updated_info['last_update'] = CURRENT_TIME;
$updated_info['last_update_by'] = ADMIN_NAME;
$this->db->update(
$this->tb_config_group_attribute_value,
$updated_info,
[
"id" => $id,
]
);
$group_id = $this->getGroupIdFromAttributeValue($id);
$this->resetProductConfigCache($group_id);
}
public function createAttributeValue($info) {
$updated_info = $info;
$updated_info['create_time'] = CURRENT_TIME;
$updated_info['create_by'] = ADMIN_NAME;
$updated_info['last_update'] = CURRENT_TIME;
$updated_info['last_update_by'] = ADMIN_NAME;
$this->db->insert($this->tb_config_group_attribute_value, $updated_info );
$group_id = $this->getGroupIdFromAttribute($info['attr_id']);
$this->resetProductConfigCache($group_id);
$this->updateAttributeValueCount($info['attr_id']);
}
//attribute
public function deleteAttribute($id, $group_id = 0) {
if(!$group_id) $group_id = $this->getGroupIdFromAttribute($id);
$this->db->runQuery("DELETE FROM `".$this->tb_config_group_attribute."` WHERE `id` = ? LIMIT 1 ", ['d'], [ $id ]);
$this->db->runQuery("DELETE FROM `".$this->tb_config_group_attribute_value."` WHERE `attr_id` = ? LIMIT 1 ", ['d'], [ $id ]);
$this->updateGroupAttributeCount($group_id);
//todo: update for product attribute_config ?
$this->resetProductConfigCache($group_id);
}
public function updateAttribute($id, $info) {
$updated_info = $info;
$updated_info['last_update'] = CURRENT_TIME;
$updated_info['last_update_by'] = ADMIN_NAME;
$this->db->update(
$this->tb_config_group_attribute,
$updated_info,
[
'id' => $id,
]
);
$group_id = $this->getGroupIdFromAttribute($id);
$this->resetProductConfigCache($group_id);
}
public function createAttribute($info) {
$updated_info = $info;
$updated_info['create_time'] = CURRENT_TIME;
$updated_info['create_by'] = ADMIN_NAME;
$updated_info['last_update'] = CURRENT_TIME;
$updated_info['last_update_by'] = ADMIN_NAME;
$this->db->insert($this->tb_config_group_attribute, $updated_info );
$this->resetProductConfigCache($info['group_id']);
$this->updateGroupAttributeCount($info['group_id']);
return $this->db->get_insert_id();
}
private function getGroupIdFromAttributeValue($att_value_id) {
$attr_id = 0;
$query = $this->db->runQuery("SELECT `attr_id` FROM `".$this->tb_config_group_attribute_value."` WHERE `id` = ? LIMIT 1 ", ['d'], [ $att_value_id ]);
if ($info = $this->db->fetchAssoc($query)) {
$attr_id = $info['attr_id'];
}
return $this->getGroupIdFromAttribute($attr_id);
}
private function getGroupIdFromAttribute($attr_id) {
$group_id = 0;
$query = $this->db->runQuery("SELECT `group_id` FROM `".$this->tb_config_group_attribute."` WHERE `id` = ? LIMIT 1 ", ['d'], [ $attr_id ]);
if ($info = $this->db->fetchAssoc($query)) {
$group_id = $info['group_id'];
}
return $group_id;
}
//create or update product in a group
public function createProduct($product_id, $group_id, array $attribute_config) {
if($this->isProductInGroup($product_id, $group_id)) {
$this->updateProduct($product_id, $group_id, $attribute_config);
}else {
$updated_info = [
"product_id" => $product_id,
"group_id" => $group_id,
"attribute_config" => json_encode($attribute_config),
"create_time" => CURRENT_TIME,
"create_by" => ADMIN_NAME,
"last_update" => CURRENT_TIME,
"last_update_by" => ADMIN_NAME,
];
$this->db->insert($this->tb_config_group_product, $updated_info );
$this->updateGroupProductCount($group_id);
}
$this->resetProductConfigCache($group_id);
}
public function deleteProduct($product_id, $group_id) {
$this->db->runQuery(
"DELETE FROM `".$this->tb_config_group_product."` WHERE `product_id` = ? AND `group_id` = ? LIMIT 1 ",
['d', 'd'], [ $product_id, $group_id ]
);
$this->deleteProductConfigCache($product_id);
$this->updateGroupProductCount($group_id);
}
public function updateProduct($product_id, $group_id, array $attribute_config, $product_name_in_group = '') {
$this->db->update(
$this->tb_config_group_product,
[
"attribute_config" => json_encode($attribute_config),
"product_name_in_group" => substr($product_name_in_group, 0, 140),
"last_update" => CURRENT_TIME,
"last_update_by" => ADMIN_NAME,
],
[
"product_id" => $product_id,
"group_id" => $group_id,
]
);
$this->resetProductConfigCache($group_id);
return true;
}
//we want to reset all caches for products in a group
//we need to do this when we make changes to group's attributes, or create new products/ update product in group
protected function resetProductConfigCache($group_id) {
$query = $this->db->runQuery("SELECT `product_id` FROM `".$this->tb_config_group_product."` WHERE `group_id` = ? ", ['d'], [$group_id]) ;
$product_list = array();
foreach( $this->db->fetchAll($query) as $item ){
$product_list[] = $item['product_id'];
}
if(sizeof($product_list)) {
$this->db->query("UPDATE `".$this->tb_config_group_product_cache."` SET
`value` = NULL
WHERE `product_id` IN (".join(",", $product_list).") ") ;
}
}
protected function deleteProductConfigCache($product_id) {
$this->db->runQuery("DELETE FROM `".$this->tb_config_group_product_cache."` WHERE `product_id` = ? LIMIT 1 ", ['d'], [$product_id]) ;
}
public function saveProductConfigCache($product_id, $value) {
$query = $this->db->runQuery(
"SELECT `product_id` FROM `".$this->tb_config_group_product_cache."` WHERE `product_id` = ? LIMIT 1 ",
['d'], [$product_id]
) ;
if($this->db->fetchAssoc($query)){
$this->db->runQuery(
"UPDATE `".$this->tb_config_group_product_cache."` SET
`value` = '".$this->db->escape(json_encode($value))."'
WHERE `product_id` = ? LIMIT 1 ",
['d'], [$product_id]
) ;
}else{
$this->db->runQuery("INSERT INTO `".$this->tb_config_group_product_cache."` (`product_id`, `value`)
VALUES ('". (int) $product_id."', '".$this->db->escape(json_encode($value))."') ") ;
}
}
public function getProductConfigCache($product_id) {
$query = $this->db->runQuery("SELECT `value` FROM `".$this->tb_config_group_product_cache."` WHERE `product_id` = ? LIMIT 1 ", ['d'], [$product_id]) ;
if($item_info = $this->db->fetchAssoc($query)){
return ($item_info['value']) ? \json_decode($item_info['value'], true) : false;
}
return false;
}
//get all group config
public function getGroupConfig($group_id) {
$query = $this->db->runQuery(
"SELECT
a.id AS attribute_id ,
a.name AS attribute_name ,
a.ordering AS attr_ordering ,
v.id AS value_id ,
v.name AS value_name ,
v.image AS image ,
v.color_code AS color ,
v.ordering ,
v.description AS description
FROM `".$this->tb_config_group_attribute."` a
LEFT JOIN `".$this->tb_config_group_attribute_value."` v ON a.id = v.attr_id
WHERE a.group_id = ?
ORDER BY attr_ordering DESC, `ordering` DESC
",
['d'], [$group_id]
);
$group_attribute = array();
foreach ( $this->db->fetchAll($query) as $info ) {
if(!isset($group_attribute[$info['attribute_id']])) {
$group_attribute[$info['attribute_id']] = array(
'id' => $info['attribute_id'],
'name' => $info['attribute_name'],
'ordering' => $info['attr_ordering'],
'list' => array(),
);
}
if($info['value_id']) {
$group_attribute[$info['attribute_id']]['list'][] = array(
'id' => $info['value_id'],
'name' => $info['value_name'],
'image' => $info['image'],
'color' => $info['color'],
'ordering' => $info['ordering'],
'description' => $info['description'],
);
}
}
return $group_attribute;
}
public function getProductConfigGroupId($product_id) {
$query = $this->db->runQuery(
"SELECT `group_id` FROM `".$this->tb_config_group_product."` WHERE `product_id` = ? LIMIT 1 ",
['d'], [$product_id]
) ;
if($item_info = $this->db->fetchAssoc($query)){
return $item_info['group_id'];
}
return 0;
}
public function getProductInGroup($group_id){
$query = $this->db->runQuery(
"SELECT `product_id`, `product_name_in_group`, `attribute_config`
FROM `".$this->tb_config_group_product."`
WHERE `group_id` = ?
",
['d'], [$group_id]
);
$product_list = array();
$product_ids = array();
foreach ( $this->db->fetchAll($query) as $info ) {
$product_ids[] = $info['product_id'];
$product_list[$info['product_id']] = array(
"id" => $info['product_id'],
"name" => "",
"product_name_in_group" => $info['product_name_in_group'],
"attribute" => \json_decode($info['attribute_config'], true),
"url" => "",
"sku" => "",
"price" => 0,
"image" => "",
"status" => "",
);
}
//find product urls
if(sizeof($product_ids)) {
$objProductModel = new ProductModel();
$product_list_info = $objProductModel->getListByIds($product_ids);
// debug_var($product_list_info);
// update $product_list
foreach ($product_list as $_pro_id => $_info) {
$_pro_info = $product_list_info[$_pro_id] ?? null;
if($_pro_info) {
$product_list[$_pro_id]['name'] = $_pro_info['title'];
$product_list[$_pro_id]['price'] = $_pro_info['price'];
$product_list[$_pro_id]['sku'] = $_pro_info['sku'];
$product_list[$_pro_id]['image'] = $_pro_info['thumbnail'];
$product_list[$_pro_id]['url'] = $_pro_info['request_path'];
$product_list[$_pro_id]['status'] = $_pro_info['status'];
}
}
}
return $product_list;
}
protected function isProductInGroup($product_id, $group_id) {
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_config_group_product."` WHERE `product_id` = ? AND `group_id` = ? LIMIT 1 ",
['d', 'd'],
[$product_id, $group_id]
) ;
return ($this->db->fetchAssoc($query));
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"q" => "",
"numPerPage" => 20,
"order_by" => '',
);*/
$catCondition = "";
return [
$catCondition,
[],
[]
];
}
protected function beforeCreateItem(array $input_info): AppResponse
{
$info = $input_info;
$info['create_time'] = CURRENT_TIME;
$info['create_by'] = ADMIN_NAME;
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function afterCreateItem($new_item_id, $new_item_info)
{
// TODO: Implement afterCreateItem() method.
}
protected function beforeUpdateItem($item_id, $current_item_info, $new_input_info):AppResponse
{
$info = $new_input_info;
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function afterUpdateItem($item_id, $old_item_info, $new_item_info)
{
// TODO: Implement afterUpdateItem() method.
}
protected function beforeDeleteItem($item_id, $item_info):AppResponse
{
return new AppResponse('ok' );
}
protected function afterDeleteItem($item_id, $item_info)
{
$query = $this->db->runQuery("SELECT `id` FROM `".$this->tb_config_group_attribute."` WHERE `group_id` = ? ", ['d'], [$item_id]);
foreach ( $this->db->fetchAll($query) as $info ) {
$this->deleteAttribute($info['id'], $item_id);
}
$this->db->runQuery(
"DELETE FROM `".$this->tb_config_group_product_cache."` WHERE `product_id` IN (SELECT product_id FROM config_group_product WHERE `group_id` = ? ) ",
['d'], [$item_id]
) ;
$this->db->runQuery("DELETE FROM `".$this->tb_config_group_product."` WHERE `group_id` = ? ", ['d'], [$item_id]) ;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Hura8\Components\Customer\AdminController;
use Hura8\Components\Customer\Controller\bCustomerController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class ACustomerController extends bCustomerController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// TODO: Implement deleteFileBeforeDeleteItem() method.
return true;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Hura8\Components\Customer\AdminController;
use Hura8\Components\Customer\Model\CustomerGroupModel;
use Hura8\Components\Province\AdminController\AProvinceController;
use Hura8\System\Controller\aAdminEntityBaseController;
class ACustomerGroupController extends aAdminEntityBaseController
{
/* @var CustomerGroupModel $objCustomerGroupModel */
protected $objCustomerGroupModel;
public function __construct()
{
$this->objCustomerGroupModel = new CustomerGroupModel();
parent::__construct($this->objCustomerGroupModel);
}
public function getAllGroup() {
return $this->objCustomerGroupModel->getList(["numPerPage" => 1000]);
}
public function removeCustomer($customer_id, $group_id)
{
return $this->objCustomerGroupModel->removeCustomer($customer_id, $group_id);
}
public function addCustomer($customer_id, $group_id)
{
return $this->objCustomerGroupModel->addCustomer($customer_id, $group_id);
}
public function getTotalCustomer($group_id, array $condition = [])
{
return $this->objCustomerGroupModel->getTotalCustomer($group_id, $condition);
}
public function getListCustomer($group_id, array $condition = []) {
$objProvinceController = new AProvinceController();
return array_map(function ($item) use ($objProvinceController){
$item['province_name'] = $objProvinceController->getProvinceName($item['province']);
return $item;
}, $this->objCustomerGroupModel->getListCustomer($group_id, $condition));
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// TODO: Implement deleteFileBeforeDeleteItem() method.
return true;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Hura8\Components\Customer\AdminController;
use Hura8\Components\Customer\Controller\bCustomerLoyaltyController;
class ACustomerLoyaltyController extends bCustomerLoyaltyController {
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Hura8\Components\Customer\Controller;
use Hura8\Components\Customer\Model\CustomerModel;
use Hura8\Components\Province\AdminController\AProvinceController;
use Hura8\System\Controller\aEntityBaseController;
class bCustomerController extends aEntityBaseController
{
/* @var CustomerModel $objCustomerModel */
protected $objCustomerModel;
/* @var AProvinceController $objProvinceController */
protected $objProvinceController;
public function __construct()
{
$this->objCustomerModel = new CustomerModel();
$this->objProvinceController = new AProvinceController();
parent::__construct($this->objCustomerModel);
}
public function formatItemInList(array $item_info)
{
return $this->formatItemInfo($item_info);
}
public function formatItemInfo(array $item_info)
{
$info = $item_info;
$info['province_name'] = $this->objProvinceController->getProvinceName($item_info['province']);
return $info;
}
}

View File

@@ -0,0 +1,206 @@
<?php
namespace Hura8\Components\Customer\Controller;
use ClientExtend\UserLoyaltyPointCalculation;
use Hura8\Components\Customer\Model\CustomerLoyaltyModel;
class bCustomerLoyaltyController
{
public static $POINT_NAME = 'điểm';
protected $point_setting = [];
protected $point_setting_config_file = ROOT_DIR . "/config/build/customer_point.php";
protected $level_setting = [];
protected $level_setting_config_file = ROOT_DIR . "/config/client/customer_level.php";
protected $level_by = '';// point|total_purchase_value as set by constant CHANGE_CUSTOMER_LEVEL_BY
/* @var $objUserLoyaltyPointCalculation UserLoyaltyPointCalculation */
protected $objUserLoyaltyPointCalculation;
/* @var CustomerLoyaltyModel $objCustomerLoyaltyModel */
protected $objCustomerLoyaltyModel;
public function __construct()
{
$this->objCustomerLoyaltyModel = new CustomerLoyaltyModel();
// import settings
//$new_info_file = "../config/build/customer_point.php" ;
//$config_file = ROOT_DIR . "/config/build/customer_point.php";
if(@file_exists($this->point_setting_config_file)) {
$this->point_setting = include $this->point_setting_config_file;
}
// customer level based on point gain
if( defined("ENABLE_CUSTOMER_POINT") && ENABLE_CUSTOMER_POINT ) {
if(@file_exists($this->level_setting_config_file)) {
$this->level_setting = include $this->level_setting_config_file;
}
}
// default is point
$this->level_by = (defined('CHANGE_CUSTOMER_LEVEL_BY')) ? CHANGE_CUSTOMER_LEVEL_BY : 'point';
$this->objUserLoyaltyPointCalculation = new UserLoyaltyPointCalculation($this->point_setting);
}
public function getPointSettingConfigFile() {
return $this->point_setting_config_file;
}
public function getPointSetting() {
return $this->point_setting;
}
public function getLevelSetting(){
return $this->level_setting;
}
// show estimate cart point
public function getEstimateCartPoint($cart_value){
$conversion_rate = (isset($this->point_setting['reward']['buy']['rate'])) ? $this->point_setting['reward']['buy']['rate'] : 0;
return ($conversion_rate) ? round($cart_value / $conversion_rate) : 0;
}
public function getUserPoint($user_id, array $condition, $return_type)
{
return $this->objCustomerLoyaltyModel->getUserPoint($user_id, $condition, $return_type);
}
// remove rewarded point i.e. reward for successfull order and now order is marked canceled
public function reclaimRewardedPoint($user_id, $activity_type_tracker){
// todo:
}
public function usePoint($user_id, $use_point, $activity_type, $activity_type_tracker, $reason = '', $point_args = ['order_value' => 0]){
// no user or no config
if(!$user_id || !ENABLE_CUSTOMER_POINT) return false;
$result = $this->objUserLoyaltyPointCalculation->calculateUsePoint($user_id, $use_point, $activity_type, $activity_type_tracker, $point_args);
$this->pointOp('use', $user_id, $result['use_point'], $activity_type, $activity_type_tracker, $reason);
return $result;
}
public function rewardPoint($user_id, $activity_type, $activity_type_tracker, $reason = '', $point_args = ['order_id' => 0]){
// no user or no config
if(!$user_id || !ENABLE_CUSTOMER_POINT) return false;
$point = $this->objUserLoyaltyPointCalculation->calculateRewardPoint($user_id, $activity_type, $activity_type_tracker, $point_args);
$this->pointOp('reward', $user_id, $point, $activity_type, $activity_type_tracker, $reason);
return $point;
}
// $operation: reward|use
// $change_point: positive (reward) or nagative (use)
protected function pointOp($operation, $user_id, $change_point, $activity_type, $activity_type_tracker, $reason = '') {
if(!$change_point) return false;
$reason_prefix = ($operation == 'use') ? 'Sử dụng' : 'Thưởng';
if($activity_type == 'return') $reason_prefix = 'Hoàn lại';
$full_reason = join(" ", [$reason_prefix, $change_point, static::$POINT_NAME, ":", $reason]);
if($operation == 'use') $change_point = -1 * $change_point;
// security: hash the row to avoid editing point directly in the database
$hash_value = sha1(join(".", [
$operation,
$user_id,
$change_point,
$activity_type,
$activity_type_tracker,
CURRENT_TIME,
'ass@ss'
]));
$new_id = $this->db->insert(
$this->tb_point ,
[
'customer_id' => $user_id ,
'activity_type' => $activity_type,
'activity_type_tracker' => $activity_type_tracker ,
'operation' => $operation,
'point' => $change_point,
'create_time' => CURRENT_TIME,
'reason' => substr($full_reason, 0, 200),
'referer_url' => substr(REFERER_URL, 0, 150) ,
'hash_value' => $hash_value ,
]
);
//update user reward balance
if($new_id) {
$this->updateStat($user_id, $change_point);
}
return $new_id;
}
protected function updateStat($user_id, $changed_point, $changed_order_value=0) {
return $this->objCustomerLoyaltyModel->updateStat($user_id, $changed_point, $changed_order_value);
}
private function calculateLevelByPoint($point) {
//if the point in between -> return the lowest level
$all_level = array_keys($this->level_setting);
foreach ( $all_level as $level) {
$next_level = $level + 1;
if(!in_array($next_level, $all_level)) $next_level = 0;
if($next_level) {
if( $point >= $this->level_setting[$level]["point_require"]
&& $point < $this->level_setting[$next_level]["point_require"] ) {
return $level;
}
}else{
if($point >= $this->level_setting[$level]["point_require"]) {
return $level;
}
}
}
return 0;
}
private function calculateLevelByOrderValue($aggregate_purchase_value = 0) {
//if the point in between -> return the lowest level
$all_level = array_keys($this->level_setting);
//tinh hang thanh vien theo so tien tich luy
foreach ( $all_level as $level ) {
$next_level = $level + 1;
if(!in_array($next_level, $all_level)) $next_level = 0;
if($next_level) {
if( $aggregate_purchase_value >= $this->level_setting[$level]["total_order_value"] &&
$aggregate_purchase_value < $this->level_setting[$next_level]["total_order_value"]
) {
return $level;
}
}else{
if( $aggregate_purchase_value >= $this->level_setting[$level]["total_order_value"]) {
return $level;
}
}
}
return 0;
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace Hura8\Components\Customer\Model;
use Hura8\System\Model\AuthModel;
class CustomerAuthModel extends AuthModel
{
private $tb_customer_login = "tb_customer_login";
private $tb_customer_access_code = "tb_customer_access_code";
private $tb_customer_login_log = "tb_customer_login_log";
public function __construct() {
parent::__construct($this->tb_customer_login, $this->tb_customer_access_code);
}
public function getLoginListByIds(array $staff_ids) {
if(!sizeof($staff_ids)) {
return [];
}
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($staff_ids, 'int');
$bind_values = $staff_ids;
$query = $this->db->runQuery(
"SELECT `user_id`, `last_login_time`, `last_login_ip`, `last_login_device`, `last_login_browser`
FROM ".$this->tb_customer_login."
WHERE `user_id` IN (".$parameterized_ids.") ",
$bind_types,
$bind_values
);
$item_list = [];
foreach ($this->db->fetchAll($query) as $item) {
$item_list[$item['user_id']] = $item;
}
return $item_list;
}
public function getLoginLog(array $conditions = []) {
$bind_types = [];
$bind_values = [];
$query = $this->db->runQuery(
"SELECT * FROM ".$this->tb_customer_login_log." WHERE 1 ORDER BY `id` DESC LIMIT 100 ",
$bind_types,
$bind_values
);
return $this->db->fetchAll($query) ;
}
/**
* @param $email
* @param string $login_status ok or error
* @param string $login_msg
*/
public function logLogin($email, $login_status, $login_msg) {
$this->db->insert(
$this->tb_customer_login_log,
[
"email" => substr($email, 0, 45),
"login_status" => $login_status,
"login_msg" => substr($login_msg, 0, 45),
"ip_address" => substr(USER_IP, 0, 45),
"user_agent" => substr(USER_AGENT, 0, 99),
"create_time" => CURRENT_TIME,
]
);
}
}

View File

@@ -0,0 +1,213 @@
<?php
namespace Hura8\Components\Customer\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iEntityModel;
use Hura8\System\Controller\UrlManagerController;
use Hura8\System\IDGenerator;
use Hura8\System\Model\aEntityBaseModel;
class CustomerGroupModel extends aEntityBaseModel implements iEntityModel
{
protected $tb_customer_per_group = "tb_customer_per_group";
public function __construct() {
parent::__construct('customer_group');
}
public function updateItemCount($group_id) {
$this->db->runQuery(
"UPDATE `".$this->tb_entity."` SET
`customer_count` = (SELECT COUNT(*) AS total FROM `".$this->tb_customer_per_group."` WHERE `group_id` = ? )
WHERE `id` = ? LIMIT 1
",
['d', 'd'], [$group_id, $group_id]
);
}
public function getTotalCustomer($group_id, array $condition = [])
{
$query = $this->db->runQuery(
" SELECT COUNT(*) as total FROM `".$this->tb_customer_per_group."` WHERE `group_id` = ? ",
['d'], [$group_id]
);
$total = 0;
if ($rs = $this->db->fetchAssoc($query)) {
$total = $rs['total'];
}
return $total;
}
public function getListCustomer($group_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 = " `id` DESC";
$query = $this->db->runQuery(
"SELECT `customer_id` FROM ".$this->tb_customer_per_group." WHERE `group_id` = ?
ORDER BY ".$order_by."
LIMIT ".(($page-1) * $numPerPage).", ".$numPerPage ,
['d'], [$group_id]
) ;
$item_list_ids = array_map(function ($item){ return $item['customer_id'];}, $this->db->fetchAll($query));
$objCustomerModel = new CustomerModel();
$list_info = $objCustomerModel->getListByIds($item_list_ids);
// final list
$final_list = [];
foreach ($item_list_ids as $_id) {
$final_list[] = $list_info[$_id] ?? null;
}
return $final_list;
}
public function removeCustomerFromAllGroup($customer_id)
{
$this->db->runQuery(
"DELETE FROM `".$this->tb_customer_per_group."` WHERE `customer_id` = ?",
['d'], [$customer_id]
);
return true;
}
public function removeCustomer($customer_id, $group_id)
{
$this->db->runQuery(
"DELETE FROM `".$this->tb_customer_per_group."` WHERE `group_id` =? AND `customer_id` = ? LIMIT 1 ",
['d', 'd'],
[$group_id, $customer_id]
);
$this->updateItemCount($group_id);
return true;
}
public function addCustomer($customer_id, $group_id)
{
$query = $this->db->runQuery(
" SELECT * FROM `".$this->tb_customer_per_group."` WHERE `group_id` = ? AND `customer_id` = ? LIMIT 1 ",
['d', 'd'],
[$group_id, $customer_id]
);
if ($this->db->fetchAssoc($query)) {
return false;
}
$this->db->insert(
$this->tb_customer_per_group,
[
"group_id" => $group_id,
"customer_id" => $customer_id,
]
);
$this->updateItemCount($group_id);
return true;
}
protected function _buildQueryConditionExtend(array $filter_condition): ?array
{
/*$condition = array(
"q" => "",
"status" => 0,
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
protected function beforeCreateItem(array $input_info) : AppResponse
{
$info = $input_info;
if(!$info['group_code']) $info['group_code'] = $info['title'];
$info['group_code'] = $this->createUniqueCode(0, $info['group_code']);
$info['create_time'] = CURRENT_TIME;
$info['create_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function afterCreateItem($new_item_id, $new_item_info)
{
}
protected function beforeUpdateItem($item_id, $current_item_info, $new_input_info): AppResponse
{
$info = $new_input_info;
if(isset($info['group_code'])) {
if(!$info['group_code']) $info['group_code'] = $info['title'];
$info['group_code'] = $this->createUniqueCode($item_id, $info['group_code']);
}
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function createUniqueCode($current_item_id, $wanted_code){
$clean_code = UrlManagerController::create_url_index($wanted_code);
//if exist and belong other id, create a new one
$query = $this->db->runQuery("SELECT `id` FROM `".$this->tb_entity."` WHERE `group_code` = ? LIMIT 1 ", ['s'], [$clean_code]) ;
if($info = $this->db->fetchAssoc($query)){
if($info['id'] != $current_item_id) {
$new_code = $clean_code."-".IDGenerator::createStringId(3);
return $this->createUniqueCode($current_item_id, $new_code);
}
}
return $clean_code;
}
protected function afterUpdateItem($item_id, $old_item_info, $new_item_info)
{
}
protected function beforeDeleteItem($item_id, $item_info) : AppResponse
{
return new AppResponse('ok');
}
protected function afterDeleteItem($item_id, $item_info)
{
}
protected function extendedFilterOptions(): array
{
return [];
}
}

View File

@@ -0,0 +1,268 @@
<?php
namespace Hura8\Components\Customer\Model;
use ClientExtend\UserLoyaltyPointCalculation;
use Hura8\Database\iConnectDB;
use Hura8\Interfaces\TableName;
class CustomerLoyaltyModel
{
/* @var iConnectDB $db */
protected $db;
protected $tb_point = TableName::CUSTOMER_POINT; // "idv_customer_point";
protected $tb_customer = TableName::CUSTOMER; // "idv_customer";
public static $POINT_NAME = 'điểm';
protected $point_setting = [];
protected $point_setting_config_file = ROOT_DIR . "/config/build/customer_point.php";
protected $level_setting = [];
protected $level_setting_config_file = ROOT_DIR . "/config/client/customer_level.php";
protected $level_by = '';// point|total_purchase_value as set by constant CHANGE_CUSTOMER_LEVEL_BY
/* @var $objUserLoyaltyPointCalculation UserLoyaltyPointCalculation */
protected $objUserLoyaltyPointCalculation;
public function __construct()
{
$this->db = get_db("", ENABLE_DB_DEBUG);
// import settings
//$new_info_file = "../config/build/customer_point.php" ;
//$config_file = ROOT_DIR . "/config/build/customer_point.php";
if(@file_exists($this->point_setting_config_file)) {
$this->point_setting = include $this->point_setting_config_file;
}
// customer level based on point gain
if( defined("ENABLE_CUSTOMER_POINT") && ENABLE_CUSTOMER_POINT ) {
if(@file_exists($this->level_setting_config_file)) {
$this->level_setting = include $this->level_setting_config_file;
}
}
// default is point
$this->level_by = (defined('CHANGE_CUSTOMER_LEVEL_BY')) ? CHANGE_CUSTOMER_LEVEL_BY : 'point';
$this->objUserLoyaltyPointCalculation = new UserLoyaltyPointCalculation($this->point_setting);
}
public function getPointSettingConfigFile() {
return $this->point_setting_config_file;
}
public function getPointSetting() {
return $this->point_setting;
}
public function getLevelSetting(){
return $this->level_setting;
}
// show estimate cart point
public function getEstimateCartPoint($cart_value){
$conversion_rate = (isset($this->point_setting['reward']['buy']['rate'])) ? $this->point_setting['reward']['buy']['rate'] : 0;
return ($conversion_rate) ? round($cart_value / $conversion_rate) : 0;
}
public function getUserPoint($user_id, array $condition, $return_type)
{
if($return_type == "total") {
//Lay tong so
$query = $this->db->runQuery("SELECT COUNT(*) AS total FROM `". $this->tb_point ."` WHERE `customer_id` = ? " , ['d'], [$user_id]);
if($resultTotal = $this->db->fetchAssoc($query)){
return $resultTotal['total'];
}
return 0;
} else {
$numPerPage = (isset($condition['numPerPage'])) ? intval($condition['numPerPage']) : 50;
$page = getPageId();
$query = $this->db->runQuery("
SELECT * FROM `". $this->tb_point ."`
WHERE `customer_id` = ?
ORDER BY id DESC
LIMIT ".($page - 1) * $numPerPage .", ".$numPerPage." " , ['d'], [$user_id]);
$result = array();
$i = ($page - 1) * $numPerPage;
foreach ( $this->db->fetchAll($query) as $rs){
$i++;
$rs['counter'] = $i;
$rs['activity_type_name'] = (isset($this->point_setting[$rs['operation']][$rs['activity_type']])) ? $this->point_setting[$rs['operation']][$rs['activity_type']]['name'] : '--';
$result[] = $rs;
}
return $result;
}
}
public function usePoint($user_id, $use_point, $activity_type, $activity_type_tracker, $reason = '', $point_args = ['order_value' => 0]){
// no user or no config
if(!$user_id || !ENABLE_CUSTOMER_POINT) return false;
$result = $this->objUserLoyaltyPointCalculation->calculateUsePoint($user_id, $use_point, $activity_type, $activity_type_tracker, $point_args);
$this->pointOp('use', $user_id, $result['use_point'], $activity_type, $activity_type_tracker, $reason);
return $result;
}
public function rewardPoint($user_id, $activity_type, $activity_type_tracker, $reason = '', $point_args = ['order_id' => 0]){
// no user or no config
if(!$user_id || !ENABLE_CUSTOMER_POINT) return false;
$point = $this->objUserLoyaltyPointCalculation->calculateRewardPoint($user_id, $activity_type, $activity_type_tracker, $point_args);
$this->pointOp('reward', $user_id, $point, $activity_type, $activity_type_tracker, $reason);
return $point;
}
// $operation: reward|use
// $change_point: positive (reward) or nagative (use)
protected function pointOp($operation, $user_id, $change_point, $activity_type, $activity_type_tracker, $reason = '') {
if(!$change_point) return false;
$reason_prefix = ($operation == 'use') ? 'Sử dụng' : 'Thưởng';
if($activity_type == 'return') $reason_prefix = 'Hoàn lại';
$full_reason = join(" ", [$reason_prefix, $change_point, static::$POINT_NAME, ":", $reason]);
if($operation == 'use') $change_point = -1 * $change_point;
// security: hash the row to avoid editing point directly in the database
$hash_value = sha1(join(".", [
$operation,
$user_id,
$change_point,
$activity_type,
$activity_type_tracker,
CURRENT_TIME,
'ass@ss'
]));
$new_id = $this->db->insert(
$this->tb_point ,
[
'customer_id' => $user_id ,
'activity_type' => $activity_type,
'activity_type_tracker' => $activity_type_tracker ,
'operation' => $operation,
'point' => $change_point,
'create_time' => CURRENT_TIME,
'reason' => substr($full_reason, 0, 200),
'referer_url' => substr(REFERER_URL, 0, 150) ,
'hash_value' => $hash_value ,
]
);
//update user reward balance
if($new_id) {
$this->updateStat($user_id, $change_point);
}
return $new_id;
}
public function updateStat($user_id, $changed_point, $changed_order_value=0) {
$user_id = intval($user_id);
$query = $this->db->runQuery("SELECT
`loyalty_point`,
`loyalty_level`,
`total_value_success`
FROM ".$this->tb_customer."
WHERE `id` = ?
LIMIT 1 " , ['d'], [$user_id]);
if($current = $this->db->fetchAssoc($query)){
$new_point = $current['loyalty_point'] + $changed_point;
$new_purchase_value = $current['total_value_success'] + $changed_order_value;
$level = $current['loyalty_level'];
if($this->level_by == 'point' && $changed_point != 0) $level = $this->calculateLevelByPoint($new_point);
else if($changed_order_value) $level = $this->calculateLevelByOrderValue($new_purchase_value);
$this->db->update(
$this->tb_customer ,
[
'loyalty_point' => $new_point,
'loyalty_level' => $level,
'total_value_success' => $new_purchase_value,
],
[
'id' => $user_id,
],
1
);
}
return true;
}
private function calculateLevelByPoint($point) {
//if the point in between -> return the lowest level
$all_level = array_keys($this->level_setting);
foreach ( $all_level as $level) {
$next_level = $level + 1;
if(!in_array($next_level, $all_level)) $next_level = 0;
if($next_level) {
if( $point >= $this->level_setting[$level]["point_require"]
&& $point < $this->level_setting[$next_level]["point_require"] ) {
return $level;
}
}else{
if($point >= $this->level_setting[$level]["point_require"]) {
return $level;
}
}
}
return 0;
}
private function calculateLevelByOrderValue($aggregate_purchase_value = 0) {
//if the point in between -> return the lowest level
$all_level = array_keys($this->level_setting);
//tinh hang thanh vien theo so tien tich luy
foreach ( $all_level as $level ) {
$next_level = $level + 1;
if(!in_array($next_level, $all_level)) $next_level = 0;
if($next_level) {
if( $aggregate_purchase_value >= $this->level_setting[$level]["total_order_value"] &&
$aggregate_purchase_value < $this->level_setting[$next_level]["total_order_value"]
) {
return $level;
}
}else{
if( $aggregate_purchase_value >= $this->level_setting[$level]["total_order_value"]) {
return $level;
}
}
}
return 0;
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace Hura8\Components\Customer\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iSearch;
use Hura8\System\IDGenerator;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Security\DataValidator;
class CustomerModel extends aEntityBaseModel implements iEntityModel
{
protected $user_types = [
"auto" => "Chưa đăng ký",
"register" => "Đăng ký thành viên",
];
/* @var iSearch $objSearchModel */
protected $objSearchModel;
public function __construct() {
$this->objSearchModel = new CustomerSearchModel();
parent::__construct(
EntityType::CUSTOMER,
"",
$this->objSearchModel
);
}
protected function extendedFilterOptions() : array
{
return [
'province' => 0,
'user_type' => '',
];
}
public function getInfoByCRMCode($code)
{
$query = $this->db->runQuery("SELECT * FROM `".$this->tb_entity."` WHERE `crm_code` = ? LIMIT 1 ", ['s'], [$code]) ;
if( $item_info = $this->db->fetchAssoc($query)){
return $this->formatItemInfo($item_info);
}
return false;
}
public function getInfoByEmail($email, $user_type='register')
{
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` WHERE `email` = ? AND `type` = ? LIMIT 1 ",
['s', 's'], [$email, $user_type]
) ;
if( $item_info = $this->db->fetchAssoc($query)){
return $this->formatItemInfo($item_info);
}
return null;
}
public function getInfoByVerifiedEmail($email)
{
$info = $this->getInfoByEmail($email);
if($info && $info['is_email_verify']) {
return $info;
}
return null;
}
protected function _buildQueryConditionExtend(array $filter_condition): ?array
{
/*$condition = array(
"user_type" => ''
"q" => "",
"status" => 0,
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
if(isset($filter_condition["province"]) && $filter_condition["province"]) {
$catCondition[] = " AND `province` = ? ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition["province"];
}
// user_type
if(isset($filter_condition["user_type"]) && array_key_exists($filter_condition["user_type"], $this->user_types) ){
$catCondition[] = " AND `type` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["user_type"];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
protected function formatItemInfo(array $item_info): array
{
if($item_info["birth_day"] && $item_info["birth_year"]) {
$item_info["birth_day"] = $item_info["birth_day"]."-".$item_info["birth_year"];
}
return $item_info;
}
protected function createWebCRMCode($input_code = '') {
return $input_code ?: "Web-".IDGenerator::createStringId(8);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Hura8\Components\Customer\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class CustomerSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
"type" => "tb_customer.type",
"province" => "tb_customer.province",
"is_email_verify" => "tb_customer.is_email_verify",
"total_value" => "tb_customer.total_value",
];
private $fulltext_fields = [
"keywords" => ["tb_customer.name", "tb_customer.crm_code", "tb_customer.email","tb_customer.mobile",],
];
public function __construct()
{
parent::__construct(
"tb_customer",
$this->fulltext_fields,
$this->filter_fields
);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Hura8\Components\Deal\AdminController;
use Hura8\Components\Deal\Model\DealCollectionModel;
use Hura8\System\Controller\aAdminEntityBaseController;
class ADealCollectionController extends aAdminEntityBaseController
{
/* @var DealCollectionModel $objDealCollectionModel */
protected $objDealCollectionModel;
public function __construct() {
$this->objDealCollectionModel = new DealCollectionModel();
parent::__construct($this->objDealCollectionModel);
}
public function getAllProductIdInCollection($collection_id) {
return $this->objDealCollectionModel->getAllProductIdInCollection($collection_id);
}
public function getAllDealIdInCollection($collection_id) {
return $this->objDealCollectionModel->getAllDealIdInCollection($collection_id);
}
public function updateAllDealInCollection($collection_id, $price, $time){
return $this->objDealCollectionModel->updateAllDealInCollection($collection_id, $price, $time);
}
public function addProductToCollection($product_id, $collection_id){
return $this->objDealCollectionModel->addProductToCollection($product_id, $collection_id);
}
public function removeFromCollection($deal_id, $collection_id){
$this->objDealCollectionModel->removeFromCollection($deal_id, $collection_id);
}
public function addToCollection($deal_id, $collection_id){
$this->objDealCollectionModel->addToCollection($deal_id, $collection_id);
}
public function updateCollectionView($id){
$this->objDealCollectionModel->updateCollectionView($id);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Hura8\Components\Deal\AdminController;
use Hura8\Components\Deal\Controller\bDealController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class ADealController extends bDealController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function autoRenew() {
$deal_list = $this->objDealModel->getAllAutoRenewableDeal();
//then renew & log history
foreach ($deal_list as $info) {
$this->renewDeal($info['id'], $info['from_time'], $info['to_time'], $info['auto_renew_history']);
}
}
protected function renewDeal($id, $from_time, $to_time, array $auto_renew_history) {
/*$obj_from_date = new \DateTime(date("Y-m-d", $from_time));
$obj_to_date = new \DateTime(date("Y-m-d", $to_time));
$day_diff = $obj_to_date->diff($obj_from_date)->format('%a');
$obj_to_date->add(new \DateInterval('P'.$day_diff.'D'));
$new_date = $obj_to_date->format('Y-m-d');
$current_date = date("Y-m-d");
$from_time_minute = ($from_time > 0) ? date("H:i", $from_time) : "00:00";
$to_time_minute = ($to_time > 0) ? date("H:i", $to_time) : "00:00";*/
$to_time_new = CURRENT_TIME + ($to_time - $from_time);
$auto_renew_history[] = [
"renew_time" => show_datetime_from_unix(CURRENT_TIME),
"from_time" => show_datetime_from_unix(CURRENT_TIME),
"to_time" => show_datetime_from_unix($to_time_new),
];
return $this->updateFields( $id,
[
'from_time' => CURRENT_TIME,
'to_time' => $to_time_new,
'auto_renew_history' => serialize($auto_renew_history),
]
);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Hura8\Components\Deal\Controller;
use Hura8\Components\Deal\Model\DealModel;
use Hura8\Components\Product\AdminController\AProductController;
use Hura8\System\Controller\aEntityBaseController;
class bDealController extends aEntityBaseController
{
protected $objDealModel;
public function __construct() {
$this->objDealModel = new DealModel();
parent::__construct($this->objDealModel);
}
public function getList(array $condition) : array
{
$deal_list = parent::getList($condition);
$product_list_info = [];
$product_list_ids = [];
$final_list = [];
foreach ($deal_list as $item) {
if(!isset($product_list_info[$item['pro_id']])) {
$product_list_info[$item['pro_id']]= null;
$product_list_ids[] = $item['pro_id'];
}
$copy = $item;
$copy['product_info'] = &$product_list_info[$item['pro_id']];
$final_list[] = $copy;
}
$objAProductController = new AProductController();
foreach ($objAProductController->getListByIds($product_list_ids) as $pro_id => $info) {
$product_list_info[$pro_id] = $info;
}
return $final_list;
}
}

View File

@@ -0,0 +1,223 @@
<?php
namespace Hura8\Components\Deal\Model;
use Hura8\Components\Product\AdminController\AProductController;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\TableName;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\TimeManager;
class DealCollectionModel extends aEntityBaseModel
{
protected $tb_deal = "tb_deal";
protected $tb_collection_item = "tb_deal_collection_item";
public function __construct()
{
parent::__construct('deal_collection');
}
protected function extendedFilterOptions(): array
{
return [
];
}
protected function _buildQueryConditionExtend(array $filter_condition): ?array
{
$where_clause = [];
$bind_types = [];
$bind_values = [];
return [
join(" ", $where_clause),
$bind_types,
$bind_values
];
}
public function getAllProductIdInCollection($collection_id) {
$deal_id_list = $this->getAllDealIdInCollection($collection_id);
if(!sizeof($deal_id_list)) return array();
$query = $this->db->runQuery("SELECT `pro_id` FROM `".$this->tb_deal."` WHERE `id` IN (". join(',', $deal_id_list).") ");
return array_map(function ($item){
return $item['pro_id'];
}, $this->db->fetchAll($query));
}
public function getAllDealIdInCollection($collection_id) {
$query = $this->db->runQuery("SELECT `deal_id` FROM `".$this->tb_collection_item."` WHERE `collection_id` = ? ", ['d'], [$collection_id]);
return array_map(function ($item){
return $item['deal_id'];
}, $this->db->fetchAll($query));
}
public function updateAllDealInCollection($collection_id, $price, $time){
$from_time = ($time['from_date'] != '') ? strtotime(TimeManager::convert_date_from_javascript($time['from_date'])." ".$time['from_time_minute'].":00") : 0;
$to_time = ($time['to_date'] != '') ? strtotime(TimeManager::convert_date_from_javascript($time['to_date'])." ".$time['to_time_minute'].":00") : 0;
$deal_id_list = $this->getAllDealIdInCollection($collection_id);
if(!sizeof($deal_id_list)) return false;
$query = $this->db->runQuery("SELECT `id`, `pro_id` FROM `".$this->tb_deal."` WHERE `id` IN (". join(',', $deal_id_list).") ");
$product_list_id = array();
foreach ( $this->db->fetchAll($query) as $info ) {
$product_list_id[$info['pro_id']] = $info['id'];
}
//get product prices
$query = $this->db->runQuery("
SELECT `id`, price FROM ".TableName::PRODUCT."
WHERE `id` IN (". join(',', array_keys($product_list_id)) .")
");
$objDealModel = new DealModel();
foreach ( $this->db->fetchAll($query) as $_info ) {
$product_id = $_info['id'];
$product_price = $_info['price'];
$deal_price = ($price['type'] == 'percent') ? round($product_price * (100 - $price['value']) / 100) : ($product_price - $price['value']);
//update
$objDealModel->updatePriceAndTime(
$product_list_id[$product_id],
array(
"price" => $deal_price,
"from_time" => $from_time,
"to_time" => $to_time,
)
);
}
return true;
}
public function addProductToCollection($product_id, $collection_id){
$objAProductController = new AProductController();
$product_info = $objAProductController->getInfo($product_id);
if($product_info){
$objDealModel = new DealModel();
$deal_id = $objDealModel->create(array(
"pro_id" => $product_id,
"title" => $product_info['title'],
"price" => $product_info['price'],
"quantity" => $product_info['quantity'],
"min_purchase" => 1,
"from_time" => 0,
"to_time" => 0,
"ordering" => $product_info[''],
"description" => '',
));
$this->addToCollection($deal_id, $collection_id);
return $deal_id;
}
return 0;
}
public function removeFromCollection($deal_id, $collection_id){
$this->db->runQuery(
"DELETE FROM `".$this->tb_collection_item."` WHERE `deal_id` = ? AND `collection_id` = ? ",
['d', 'd'],
[$deal_id, $collection_id]
);
$this->updateCollectionCount($collection_id);
}
public function addToCollection($deal_id, $collection_id){
$query = $this->db->runQuery(
"SELECT `deal_id` FROM `".$this->tb_collection_item."` WHERE `deal_id`= ? AND `collection_id` = ? LIMIT 1 ",
['d', 'd'], [$deal_id, $collection_id]
);
if( ! $this->db->fetchAssoc($query) ){
$this->db->insert(
$this->tb_collection_item ,
[
'collection_id' => $collection_id ,
'deal_id' => $deal_id,
'create_by' => ADMIN_ID,
'create_time' => CURRENT_TIME,
]
);
$this->updateCollectionCount($collection_id);
}
}
protected function updateCollectionCount($collection_id){
$this->db->runQuery(
"UPDATE `".$this->tb_entity."` SET
`deal_count` = (SELECT COUNT(*) FROM `".$this->tb_collection_item."` WHERE `collection_id` = ? )
WHERE `id` = ? LIMIT 1 ",
['d', 'd'],
[$collection_id, $collection_id]
);
}
public function updateCollectionView($id){
$this->db->runQuery("UPDATE `".$this->tb_entity."` SET `visit` = `visit` + 1 WHERE `id` = ? LIMIT 1 ", ['d'], [ $id ]);
}
protected function beforeCreateItem(array $input_info) : AppResponse
{
$info = $input_info;
$info['create_time'] = CURRENT_TIME;
$info['create_by'] = ADMIN_NAME;
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function afterCreateItem($new_item_id, $new_item_info)
{
// TODO: Implement afterCreateItem() method.
}
protected function beforeUpdateItem($item_id, $current_item_info, $new_input_info) : AppResponse
{
$info = $new_input_info;
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function afterUpdateItem($item_id, $old_item_info, $new_item_info)
{
// TODO: Implement afterUpdateItem() method.
}
protected function beforeDeleteItem($item_id, $item_info) : AppResponse
{
return new AppResponse('ok');
}
protected function afterDeleteItem($item_id, $item_info)
{
// TODO: Implement afterDeleteItem() method.
}
}

View File

@@ -0,0 +1,196 @@
<?php
namespace Hura8\Components\Deal\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\Security\DataValidator;
use Hura8\System\TimeManager;
class DealModel extends aEntityBaseModel
{
protected $tb_collection_item = "tb_deal_collection_item";
public function __construct()
{
parent::__construct('deal');
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getAllAutoRenewableDeal() {
$query = $this->db->runQuery(
"SELECT `id`, `from_time`, `to_time` FROM `". $this->tb_entity ."`
WHERE `to_time` < '".CURRENT_TIME."' AND `to_time` > 0 AND `auto_renew` = 1 "
);
return $this->db->fetchAll($query);
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$where_clause = [];
$bind_types = [];
$bind_values = [];
$limit_by_time = false;
$limit_by_time_condition = '';
// get deal by start time
// require format: YY-mm-dd h:m:i or YY-mm-dd h:m or timestamp
$datetime_pattern = '/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?$/i';
if(isset($filter_condition['start_time']) && $filter_condition['start_time'] ) {
$limit_by_time = true;
if(preg_match($datetime_pattern, trim($filter_condition['start_time']))) {
$check_time = strtotime($filter_condition['start_time']);
}else{
$check_time = intval($filter_condition['start_time']);
}
$limit_by_time_condition .= " AND `from_time` >= '".$check_time."' ";
$bind_types[] = 'd';
$bind_values[] = $check_time;
}
// get deal by end time
// require format: YY-mm-dd h:m:i or YY-mm-dd h:m or timestamp
if(isset($filter_condition['end_time']) && $filter_condition['end_time'] ) {
$limit_by_time = true;
if(preg_match($datetime_pattern, trim($filter_condition['end_time']))) {
$check_time = strtotime($filter_condition['end_time']);
}else{
$check_time = intval($filter_condition['end_time']);
}
$limit_by_time_condition .= " AND `to_time` <= '".$check_time."' ";
$bind_types[] = 'd';
$bind_values[] = $check_time;
}
if($limit_by_time) {
$where_clause[] = " AND `status`= 1 ".$limit_by_time_condition;
}
//type expire
if(isset($filter_condition['type']) && $filter_condition['type'] == 'expire' ) {
$where_clause[] = "AND `status`= 1 AND `to_time` < ? ";
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
}
//type active: might not have begun yet
if(isset($filter_condition['type']) && $filter_condition['type'] == 'active' && !$limit_by_time ) {
$where_clause[] = " AND `status` = 1 AND `to_time` >= ? ";
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
}
// Đang bắt đầu
if(isset($filter_condition['type']) && $filter_condition['type'] == 'started' && !$limit_by_time ) {
$where_clause[] = " AND `status` = 1 AND `to_time` >= ? AND from_time < ? ";
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
}
// Chưa bắt đầu
if(isset($filter_condition['type']) && $filter_condition['type'] == 'coming' && !$limit_by_time ) {
$where_clause[] = " AND `status` = 1 AND `from_time` >= ? ";
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
}
//deal collection
if(isset($filter_condition['collection_id']) && $filter_condition['collection_id'] > 0) {
$where_clause[] = " AND `id` IN ( SELECT `deal_id` FROM `".$this->tb_collection_item."` WHERE `collection_id` = ? ) ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition['collection_id'];
}
// exclude from collection
if(isset($filter_condition['add_to_collection']) && $filter_condition['add_to_collection'] > 0) {
$where_clause[] = " AND `id` NOT IN ( SELECT `deal_id` FROM `".$this->tb_collection_item."` WHERE `collection_id` = ? ) ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition['add_to_collection'];
}
// by word filter
$filter = $filter_condition['filter'] ?? '';
switch ($filter) {
case "not-started": // Chưa bắt đầu
$where_clause[] = " AND ( ? - `from_time` ) < 0 ";
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
break;
case "started": // Đang bắt đầu
$where_clause[] = " AND ( ? - `from_time` ) > 0 AND ( `to_time` - ?) > 0 ";
$bind_types[] = 'd';
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
$bind_values[] = CURRENT_TIME;
break;
case "ended": // Hết thời gian
$where_clause[] = " AND (`to_time` - ?) < 0 ";
$bind_types[] = 'd';
$bind_values[] = CURRENT_TIME;
break;
case "hidden": // Ẩn hiển thị
$where_clause[] = " AND `status` = 0 ";
break;
case "featured": // Đang nổi bật
$where_clause[] = " AND `is_featured` = 1 ";
break;
}
return [
join(" ", $where_clause),
$bind_types,
$bind_values
];
}
protected function formatItemInList(array $item_info): array
{
$copy = $item_info;
$copy['deal_time_happen'] = CURRENT_TIME - $item_info['from_time'];
$copy['deal_time_left'] = $item_info['to_time'] - CURRENT_TIME;
$copy['is_start'] = (CURRENT_TIME - $item_info['from_time'] > 0) ? 1 : 0;
$copy['is_end'] = ($item_info['to_time'] - CURRENT_TIME > 0) ? 0 : 1;
return $copy;
}
protected function formatItemInfo(array $item_info): array
{
$copy = $item_info;
$copy['deal_time_happen'] = CURRENT_TIME - $item_info['from_time'];
$copy['deal_time_left'] = $item_info['to_time'] - CURRENT_TIME;
$copy['is_start'] = (CURRENT_TIME - $item_info['from_time'] > 0) ? 1 : 0;
$copy['is_end'] = ($item_info['to_time'] - CURRENT_TIME > 0) ? 0 : 1;
return $copy;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Hura8\Components\Marketing\AdminController;
use Hura8\Components\Marketing\Controller\bCouponController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class ACouponController extends bCouponController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function updateProduct($product_id, $coupon_id, array $info)
{
return $this->objCouponModel->updateProduct($product_id, $coupon_id, $info);
}
public function removeProduct($product_id, $coupon_id)
{
return $this->objCouponModel->removeProduct($product_id, $coupon_id);
}
public function addProduct($product_id, $coupon_id, $ordering=0)
{
return $this->objCouponModel->addProduct($product_id, $coupon_id, $ordering);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// TODO: Implement deleteFileBeforeDeleteItem() method.
return true;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Hura8\Components\Marketing\AdminController;
use Hura8\Components\Marketing\Model\PosterModel;
use Hura8\System\Controller\aAdminEntityBaseController;
class APosterController extends aAdminEntityBaseController
{
/* @var PosterModel $objPosterModel */
protected $objPosterModel;
public function __construct()
{
$this->objPosterModel = new PosterModel();
parent::__construct($this->objPosterModel);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// TODO: Implement deleteFileBeforeDeleteItem() method.
return true;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Hura8\Components\Marketing\AdminController;
use Hura8\Components\Marketing\Controller\bProductFeedController;
use Hura8\Components\Marketing\Model\ProductFeedModel;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\System\Controller\aAdminEntityBaseController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class AProductFeedController extends bProductFeedController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function getAllCategories() {
return $this->objProductFeedModel->getAllCategories();
}
public function getAllProductListIds( $list_id){
return $this->objProductFeedModel->getAllProductListIds($list_id);
}
public function getProductListTotal($list_id) {
return $this->objProductFeedModel->getProductListTotal($list_id);
}
public function getProductList($list_id, $page = 1, $numPerPage = 30) {
return $this->objProductFeedModel->getProductList($list_id, $page, $numPerPage);
}
public function deleteAllProductFromList($list_id) {
$this->objProductFeedModel->deleteAllProductFromList($list_id);
}
//remove product from a list
public function deleteProductFromList($pro_list, $list_id){
return $this->objProductFeedModel->deleteProductFromList($pro_list, $list_id);
}
//add product to a list
public function addProductToList($pro_list, $list_id){
return $this->objProductFeedModel->addProductToList($pro_list, $list_id);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace Hura8\Components\Marketing\Controller;
use Hura8\Components\Marketing\Model\CouponModel;
use Hura8\System\Controller\aEntityBaseController;
class bCouponController extends aEntityBaseController
{
protected $type_list = array(
'pro' => "Tặng sản phẩm",
'cash' => "Tặng tiền mặt",
'priceoff' => "Giảm giá %",
'other' => "Khác"
);
/* @var CouponModel $objCouponModel */
protected $objCouponModel;
public function __construct()
{
$this->objCouponModel = new CouponModel();
parent::__construct($this->objCouponModel);
}
public function getTotalProduct($coupon_id, array $condition = [])
{
return $this->objCouponModel->getTotalProduct($coupon_id, $condition);
}
public function getListProduct($coupon_id, array $condition = []) {
return $this->objCouponModel->getListProduct($coupon_id, $condition);
}
public function getTypeList() {
return $this->type_list;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Hura8\Components\Marketing\Controller;
use Hura8\Components\Marketing\Model\ProductFeedModel;
use Hura8\System\Controller\aEntityBaseController;
class bProductFeedController extends aEntityBaseController
{
protected $objProductFeedModel;
public function __construct()
{
$this->objProductFeedModel = new ProductFeedModel();
parent::__construct($this->objProductFeedModel);
}
public function getInfoByPublicId($public_id)
{
return $this->objProductFeedModel->getInfoByPublicId($public_id);
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace Hura8\Components\Marketing\Model;
use Hura8\Components\Product\Model\ProductModel;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\EntityType;
use Hura8\System\IDGenerator;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
use Hura8\System\TimeManager;
class CouponModel extends aEntityBaseModel
{
protected $tb_coupon_product = "tb_coupon_product";
public function __construct() {
parent::__construct(EntityType::COUPON);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getTotalProduct($coupon_id, array $condition = [])
{
$query = $this->db->runQuery(
" SELECT COUNT(*) as total FROM `".$this->tb_coupon_product."` WHERE `coupon_id` = ? ",
['d'], [$coupon_id]
);
$total = 0;
if ($rs = $this->db->fetchAssoc($query)) {
$total = $rs['total'];
}
return $total;
}
public function getListProduct($coupon_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` FROM ".$this->tb_coupon_product." WHERE `coupon_id` = ?
ORDER BY ".$order_by."
LIMIT ".(($page-1) * $numPerPage).", ".$numPerPage ,
['d'], [$coupon_id]
) ;
$item_list = array();
$counter = ($page-1) * $numPerPage;
foreach ( $this->db->fetchAll($query) as $item ) {
$counter += 1;
$item_list[$item['product_id']] = [
'counter' => $counter,
];
}
$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;
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"q" => getRequest("q", ''),
"featured" => (int) getRequest("featured"),
"status" => (int) getRequest("status"),
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
if(isset($filter_condition["letter"]) && strlen($filter_condition["letter"]) == 1){
$catCondition[] = " AND `letter` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["letter"];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
protected function createUniqueCode($current_item_id, $wanted_code = ''){
if(!$wanted_code) $wanted_code = IDGenerator::createStringId(10);
$clean_code = DataClean::makeInputSafe($wanted_code, DataType::ID);
$clean_code = strtoupper($clean_code);
//if exist and belong other id, create a new one
$query = $this->db->runQuery("SELECT `id` FROM `".$this->tb_entity."` WHERE `code` = ? LIMIT 1 ", ['s'], [$clean_code]) ;
if($info = $this->db->fetchAssoc($query)){
if($info['id'] != $current_item_id) {
$new_code = IDGenerator::createStringId(6);
return $this->createUniqueCode($current_item_id, $new_code);
}
}
return $clean_code;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Hura8\Components\Marketing\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
class PosterModel extends aEntityBaseModel
{
public function __construct() {
parent::__construct('poster');
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$catCondition = [];
$bind_types = [];
$bind_values = [];
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Hura8\Components\Marketing\Model;
use Hura8\Components\Product\AdminController\AProductController;
use Hura8\Interfaces\AppResponse;
use Hura8\System\IDGenerator;
use Hura8\System\Model\aEntityBaseModel;
class ProductFeedModel extends aEntityBaseModel
{
protected $tb_feed_product = "tb_feed_product";
public function __construct() {
parent::__construct('feed');
//$this->all_brands = $this->buildAllBrands();
//$this->all_categories = $this->buildAllCategories();
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getInfoByPublicId($public_id)
{
$query = $this->db->runQuery("SELECT * FROM `".$this->tb_entity."` WHERE `public_id` = ? LIMIT 1 ", ['s'], [$public_id]) ;
if( $item_info = $this->db->fetchAssoc($query)){
return $this->formatItemInfo($item_info);
}
return false;
}
public function getProductListTotal($list_id) {
$query = $this->db->runQuery(
" SELECT COUNT(*) AS total FROM `".$this->tb_feed_product."` WHERE `list_id` = ? ",
['d'], [$list_id]
);
$total = 0;
if ($info = $this->db->fetchAssoc($query)) {
$total = $info['total'];
}
return $total;
}
public function getProductList($list_id, $page = 1, $numPerPage = 30) {
$query = $this->db->runQuery(
" SELECT `pro_id` FROM `".$this->tb_feed_product."`
WHERE `list_id` = ?
ORDER BY `id` DESC
LIMIT ".( ($page - 1) * $numPerPage ).", ".$numPerPage,
['d'], [$list_id]
);
$product_list_ids = array_map(function ($item) { return $item['pro_id'];}, $this->db->fetchAll($query));
$objAProductController = new AProductController();
$stt = ($page - 1) * $numPerPage;
$item_list = [];
foreach ($objAProductController->getListByIds($product_list_ids) as $_id => $info) {
$stt++;
$info["counter"] = $stt;
$item_list[] = $info;
}
return $item_list;
}
//--------------------
public function getAllCategories() {
return $this->all_categories;
}
public function getAllProductListIds( $list_id){
if(!$list_id) return [];
$query = $this->db->runQuery("SELECT `pro_id` FROM `".$this->tb_feed_product."` WHERE `list_id` = ? ", ['d'], [ $list_id ]);
$item_list = array();
foreach ( $this->db->fetchAll($query) as $rs ) {
$item_list[] = $rs['pro_id'];
}
return $item_list;
}
//--------------------
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"letter" => "",
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
if(isset($filter_condition["letter"]) && strlen($filter_condition["letter"]) == 1){
$catCondition[] = " AND `letter` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["letter"];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Hura8\Components\Marketing\Model;
class UProductFeedModel
{
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Hura8\Components\Media\AdminController;
use Hura8\Components\Media\Controller\bItemMediaController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class AItemMediaController extends bItemMediaController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// delete thumb files
$item_info = $this->getInfo($item_id);
if(!$item_info['file_url']) {
return true;
}
$file_name = $item_info['file_url'];
foreach (static::$resized_sizes as $size => $value) {
$file_local_path = PUBLIC_DIR . "/". static::$image_folder . "/". $size. IMAGE_FILE_SEPARATOR . $file_name;
@unlink($file_local_path);
}
// remove original file
$file_local_path = PUBLIC_DIR . "/". static::$image_folder . "/". $file_name;
@unlink($file_local_path);
return true;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Hura8\Components\Media\AdminController;
use Hura8\Components\Media\Controller\bMediaCategoryController;
use Hura8\Interfaces\iEntityAdminCategoryController;
use Hura8\Traits\AdminEntityCategoryControllerTraits;
class AMediaCategoryController extends bMediaCategoryController implements iEntityAdminCategoryController
{
use AdminEntityCategoryControllerTraits;
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Hura8\Components\Media\AdminController;
use Hura8\Components\Media\Controller\bMediaController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class AMediaController extends bMediaController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public static function createUploadSubFolder() {
return date("Y-m-d");
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// delete thumb files
$item_info = $this->getInfo($item_id);
if(!$item_info['file_url']) {
return true;
}
list($sub_folder, $file_name) = explode("/", $item_info['file_url']); // format: Y-m-d/file_name
foreach (static::$resized_sizes as $size => $value) {
$file_local_path = PUBLIC_DIR . "/". static::$image_folder . "/" . $sub_folder . "/". $size. IMAGE_FILE_SEPARATOR . $file_name;
@unlink($file_local_path);
}
// remove original file
$file_local_path = PUBLIC_DIR . "/". static::$image_folder . "/" . $sub_folder . "/". $file_name;
@unlink($file_local_path);
return true;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Hura8\Components\Media\Controller;
use Hura8\Components\Media\Model\ItemMediaModel;
use Hura8\System\Controller\aEntityBaseController;
class bItemMediaController extends aEntityBaseController
{
static $image_folder = "media/item";
static $resized_sizes = array(
's' => ['width' => 300,]
);
/* @var ItemMediaModel $objItemMediaModel */
protected $objItemMediaModel;
protected $item_type;
protected $item_id;
public function __construct(string $item_type = '', $item_id = 0)
{
$this->objItemMediaModel = new ItemMediaModel($item_type, $item_id);
parent::__construct($this->objItemMediaModel);
}
protected function formatItemInList(array $item_info) : array
{
return $this->formatItemInfo($item_info);
}
protected function formatItemInfo(array $item_info) : ?array
{
if(!$item_info) return null;
$item_info['display_file'] = STATIC_DOMAIN . "/". static::$image_folder. "/". $item_info['file_url'];
$item_info['image'] = self::getResizedImageCollection($item_info['file_url']);
return $item_info;
}
public static function getResizedImageCollection($image_name) {
$image = [];
$size_in_full = [
's' => 'small' ,
];
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 : '';
}
return $image;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Hura8\Components\Media\Controller;
use Hura8\System\Controller\aCategoryBaseController;
use Hura8\Components\Media\Model\MediaCategoryModel;
class bMediaCategoryController extends aCategoryBaseController
{
protected $objMediaCategoryModel;
public function __construct()
{
$this->objMediaCategoryModel = new MediaCategoryModel();
parent::__construct($this->objMediaCategoryModel);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Hura8\Components\Media\Controller;
use Hura8\System\Controller\aAdminEntityBaseController;
use Hura8\Components\Media\Model\MediaModel;
use Hura8\System\Controller\aEntityBaseController;
class bMediaController extends aEntityBaseController
{
static $image_folder = "media/lib";
static $resized_sizes = array(
's' => ['width' => 300,]
);
protected $objMediaModel;
public function __construct()
{
$this->objMediaModel = new MediaModel();
parent::__construct($this->objMediaModel);
}
protected function formatItemInList(array $item_info) : array
{
return $this->formatItemInfo($item_info);
}
protected function formatItemInfo(array $item_info) : ?array
{
if(!$item_info) return null;
$item_info['display_file'] = STATIC_DOMAIN . "/". static::$image_folder. "/". $item_info['file_url'];
$item_info['image'] = ($item_info['file_type'] == 'image') ? self::getResizedImageCollection($item_info['file_url']) : null;
return $item_info;
}
public static function getResizedImageCollection($image_name) {
$image = [];
$size_in_full = [
's' => 'small' ,
];
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 : '';
}
return $image;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Hura8\Components\Media\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\System\Url;
class ItemMediaModel extends aEntityBaseModel implements iEntityModel
{
protected $item_type;
protected $item_id;
public function __construct(string $item_type = '', $item_id = 0) {
parent::__construct('item-media');
$this->item_type = $item_type;
$this->item_id = $item_id;
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
$catCondition = [];
$bind_types = [];
$bind_values = [];
if($this->item_type) {
$catCondition[] = " AND `item_type` = ? ";
$bind_types[] = 's';
$bind_values[] = $this->item_type;
}
if($this->item_id) {
$catCondition[] = " AND `item_id` = ? ";
$bind_types[] = 'd';
$bind_values[] = $this->item_id;
}
if(isset($filter_condition["item_type"]) && $filter_condition["item_type"]){
$catCondition[] = " AND `item_type` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["item_type"];
}
if(isset($filter_condition["item_id"]) && $filter_condition["item_id"]){
$catCondition[] = " AND `item_id` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["item_id"];
}
if(isset($filter_condition["file_type"]) && $filter_condition["file_type"]){
$catCondition[] = " AND `file_type` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["file_type"];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Hura8\Components\Media\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class ItemMediaSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
'item_type' => "tb_item_media.item_type",
'item_id' => "tb_item_media.item_id",
'file_type' => "tb_item_media.file_type",
];
private $fulltext_fields = [
"keywords" => ["tb_item_media.title",],
];
public function __construct()
{
parent::__construct(
"tb_item_media",
$this->fulltext_fields,
$this->filter_fields
);
//$this->createTableSearch();
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Hura8\Components\Media\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aCategoryBaseModel;
use Hura8\Interfaces\iEntityCategoryModel;
class MediaCategoryModel extends aCategoryBaseModel implements iEntityCategoryModel
{
protected $tb_media_per_category = "tb_media_per_category";
public function __construct() {
parent::__construct('media-category');
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Hura8\Components\Media\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Url;
class MediaModel extends aEntityBaseModel implements iEntityModel
{
protected $tb_media_per_category = "tb_media_per_category";
public function __construct() {
parent::__construct(EntityType::MEDIA, '', new MediaSearchModel());
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
protected function _buildQueryConditionExtend(array $filter_condition) : ?array
{
/*$condition = array(
"category" => getRequestInt("category"),
"file_type" => '',
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
//Tim danh muc
if(isset($filter_condition["category"]) && $filter_condition["category"]){
$catCondition[] = " AND `id` IN (SELECT `item_id` FROM `".$this->tb_media_per_category."` WHERE `category_id` = ?) ";
$bind_types[] = 'd';
$bind_values[] = $filter_condition["category"];
}
if(isset($filter_condition["file_type"]) && $filter_condition["file_type"]){
$catCondition[] = " AND `file_type` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["file_type"];
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Hura8\Components\Media\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class MediaSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
'file_type' => "tb_media.file_type",
];
private $fulltext_fields = [
"keywords" => ["tb_media.title",],
];
public function __construct()
{
parent::__construct(
"tb_media",
$this->fulltext_fields,
$this->filter_fields
);
//$this->createTableSearch();
}
}

View File

@@ -0,0 +1,235 @@
<?php
namespace Hura8\Components\Order\AdminController;
use Hura8\Components\Order\Controller\OrderStatus;
use Hura8\Components\Order\Model\OrderModel;
use Hura8\Events\EventName;
use Hura8\Events\RealEvents\OrderEvent;
use Hura8\System\Controller\aAdminEntityBaseController;
class AOrderController extends aAdminEntityBaseController
{
/* @var OrderModel $objOrderModel */
protected $objOrderModel;
/* @var AOrderStatusController $objAOrderStatusController */
protected $objAOrderStatusController;
public function __construct()
{
$this->objAOrderStatusController = new AOrderStatusController();
$this->objOrderModel = new OrderModel();
parent::__construct($this->objOrderModel);
}
public function updateOrderFulfillmentStatus($order_id, $new_status, $comment, $payment_data = []) {
if(!array_key_exists($new_status, OrderStatus::FULFILLMENT_STATUS)) {
return [
"status" => 'error',
'message' => '',
];
}
$this->objAOrderStatusController->createHistory($order_id, 'fulfillment', $new_status, $comment, $payment_data );
//cap nhat don hang
$this->objOrderModel->update( $order_id, [
'fulfillment_status' => $new_status
]);
$this->updateDerivedOrderStatus($order_id);
return [
"status" => 'success',
'message' => 'Cập nhật thành công',
];
}
public function updateOrderPaymentStatus($order_id, $new_status, $comment, $payment_data = []) {
if(!array_key_exists($new_status, OrderStatus::PAYMENT_STATUS)) {
return [
"status" => 'error',
'message' => '',
];
}
$this->objAOrderStatusController->createHistory($order_id, 'payment', $new_status, $comment, $payment_data );
//cap nhat don hang
$this->objOrderModel->update( $order_id, [
'payment_status' => $new_status
]);
$this->updateDerivedOrderStatus($order_id);
return [
"status" => 'success',
'message' => 'Cập nhật thành công',
];
}
protected function updateDerivedOrderStatus($order_id) {
$current_info = $this->getInfo($order_id);
// order must be success
if(
$current_info['payment_status'] == OrderStatus::PAYMENT_STATUS['paid']['id'] &&
$current_info['fulfillment_status'] == OrderStatus::FULFILLMENT_STATUS['fulfilled']['id']
) {
return $this->objOrderModel->update($order_id, [
'order_status' => OrderStatus::ORDER_STATUS['success']['id']
]);
}
// update processing
return $this->objOrderModel->update($order_id, [
'order_status' => OrderStatus::ORDER_STATUS['processing']['id']
]);
}
public function updateOrderStatus($order_id, $status, $comment) {
$current_info = $this->getInfo($order_id);
// cannot update if order has: success or cancel
if(in_array($current_info['order_status'], [
OrderStatus::ORDER_STATUS['success']['id'],
OrderStatus::ORDER_STATUS['canceled']['id'],
])) {
return [
"status" => 'error',
'message' => '',
];
}
if(!array_key_exists($status, OrderStatus::ORDER_STATUS)) {
return [
"status" => 'error',
'message' => '',
];
}
$this->objAOrderStatusController->createHistory($order_id, 'order', $status, $comment );
$this->objOrderModel->update($order_id, [
'order_status' => $status
]);
// dispatch
$objOrderEvent = new OrderEvent();
$objOrderEvent->setUpdateOrderInfo([
'orderId' => $order_id,
'order_status' => $status,
'old_status' => $current_info['order_status']
]);
get_dispatcher()->dispatch(EventName::ORDER['updated_status'], $objOrderEvent);
return [
"status" => 'success',
'message' => 'Cập nhật thành công',
];
}
protected function formatItemInList(array $order_info ) {
$rs = $order_info;
$rs["order_date"] = date("d-m-Y", $rs['order_time']);
$rs["order_hour"] = date("h:i a", $rs['order_time']);
if($rs['order_discount']) $rs['order_discount'] = \json_decode($rs['order_discount'], true);
$rs["payment_status_name"] = (array_key_exists($rs["payment_status"], OrderStatus::PAYMENT_STATUS)) ? OrderStatus::PAYMENT_STATUS[$rs["payment_status"]]['title'] : '';
$rs["fulfillment_status_name"] = (array_key_exists($rs["fulfillment_status"], OrderStatus::FULFILLMENT_STATUS)) ? OrderStatus::FULFILLMENT_STATUS[$rs["fulfillment_status"]]['title'] : '';
$rs["order_status_name"] = (array_key_exists($rs["order_status"], OrderStatus::ORDER_STATUS)) ? OrderStatus::ORDER_STATUS[$rs["order_status"]]['title'] : '';
return $rs;
}
protected function getListFilterCondition($list_id) {
if($list_id == 'mine') {
return [
'assign_to' => ADMIN_ID,
];
}
if($list_id == 'new') {
return [
'status' => 'new',
];
}
if($list_id == 'unpaid') {
return [
'payment' => 'unpaid',
];
}
if($list_id == 'partially-paid') {
return [
'payment' => 'partially-paid',
];
}
if($list_id == 'unfulfilled') {
return [
'fullfillment' => 'unfulfilled',
];
}
if($list_id == 'partially-fulfilled') {
return [
'fullfillment' => 'partially-fulfilled',
];
}
return null;
}
public function getStatusHistory($orderId){
return $this->objAOrderStatusController->getStatusHistory($orderId);
}
public function getItemsForOrderList(array $list_ids, $fields = "*") {
return $this->objOrderModel->getItemsForOrderList($list_ids, $fields);
}
public static function createId($order_id){
$key_length = strlen($order_id);
$order_keys = [];
for($i=0; $i < 9 - $key_length; $i++){
$order_keys[] = "0";
}
$order_keys[] = $order_id;
$order_eles = str_split(join("", $order_keys), 3);
return join("-", $order_eles);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
return true;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Hura8\Components\Order\AdminController;
use Hura8\Components\Order\Model\OrderStatusModel;
class AOrderStatusController
{
/* @var OrderStatusModel $objOrderStatusModel */
protected $objOrderStatusModel;
public function __construct() {
$this->objOrderStatusModel = new OrderStatusModel();
}
public function getStatusHistory($orderId){
return $this->objOrderStatusModel->getStatusHistory($orderId);
}
public function createHistory($order_id, $status_type = 'order', $system_status = '', $comment = '', array $data= [], array $other_info = []) {
return $this->objOrderStatusModel->createHistory($order_id, $status_type , $system_status , $comment, $data, $other_info);
}
public function getInfo($status_id) {
return $this->objOrderStatusModel->getInfo($status_id);
}
public function getAll() {
return $this->objOrderStatusModel->getAll();
}
public function delete($id) {
return $this->objOrderStatusModel->delete($id);
}
public function update($id, array $info) {
return $this->objOrderStatusModel->update($id, $info);
}
public function create(array $info) {
return $this->objOrderStatusModel->create($info);
}
}

View File

@@ -0,0 +1,43 @@
<?php
// https://help.shopify.com/en/manual/orders/order-status
namespace Hura8\Components\Order\Controller;
class OrderStatus
{
const ORDER_STATUS = [
'new' => ['id' => 'new', 'title' => 'Mới'],// 'Mới', //
'processing' => ['id' => 'processing', 'title' => 'Đang xử lý'],// 'Đang xử lý', //
'success' => ['id' => 'success', 'title' => 'Thành công'],// 'Thành công', //
'closed' => ['id' => 'closed', 'title' => 'Đóng lại'],// 'Đóng lại', //
//'archived' => 'Lưu kho', //
'canceled' => ['id' => 'canceled', 'title' => 'Hủy'],// 'Hủy', //
];
const PAYMENT_STATUS = [
'pending' => ['id' => 'pending', 'title' => 'Chờ'],// 'Chờ', // Orders have the Payment pending status for one of the following reasons: During checkout, You create an order, Manual payment methods, such as Cash On Delivery
'authorized' => ['id' => 'authorized', 'title' => 'Đã xác nhận'],// 'Đã xác nhận', // The payment provider validated your customers payment information. The order is created, customers can complete their checkout, and inventory is reserved.
'overdue' => ['id' => 'overdue', 'title' => 'Quá hạn'],// 'Quá hạn', // Payment wasn't captured before the due date that was set in the payment terms on an order that had the Payment pending status.
'expiring' => ['id' => 'expiring', 'title' => 'Sắp hết hạn'],// 'Sắp hết hạn', // Expiring isn't a payment status, but the Expiring badge is displayed two days before the deadline for capturing payment on orders that have the Authorized payment status.
'expired' => ['id' => 'expired', 'title' => 'Hết hạn'],// 'Hết hạn', // Payment wasn't captured before the date that was set by the payment provider on an order that had the Authorized payment status.
'paid' => ['id' => 'paid', 'title' => 'Đã thanh toán'],// 'Đã thanh toán', // Payment was automatically or manually captured, or the order was marked as paid.
'refunded' => ['id' => 'refunded', 'title' => 'Đã hoàn lại'],// 'Đã hoàn lại', // The full amount that the customer paid for an order was returned to the customer.
'partially-refunded' => ['id' => 'partially-refunded', 'title' => 'Hoàn lại 1 phần'],// 'Hoàn lại 1 phần', // The amount that was returned to a customer is less than the full amount that the customer paid for an order.
'partially-paid' => ['id' => 'partially-paid', 'title' => 'Thanh toán 1 phần'],// 'Thanh toán 1 phần', // You manually captured payment for the order and specified less than the full order value as the payment amount.
'voided' => ['id' => 'voided', 'title' => 'Hủy'],// 'Hủy', // An unpaid order was manually canceled.
'unpaid' => ['id' => 'unpaid', 'title' => 'Chưa thanh toán'],// 'Chưa thanh toán', // Unpaid payment status includes orders that are in Authorized, Pending, Expired, and Partially paid payment status.
];
const FULFILLMENT_STATUS = [
'fulfilled' => ['id' => 'fulfilled', 'title' => 'Đã chuyển hết'],// 'Đã chuyển hết', // When you've shipped all the items in an order, it is Fulfilled.
'unfulfilled' => ['id' => 'unfulfilled', 'title' => 'Chưa chuyển'],// 'Chưa chuyển', // When an order is placed, it has an Unfulfilled fulfillment status, unless you have selected to automatically capture the payment and automatically fulfill all of the order's line items in the checkout settings.
'partially-fulfilled' => ['id' => 'partially-fulfilled', 'title' => 'Chuyển 1 phần'],// 'Chuyển 1 phần', // If you have shipped some, but not all, of the items in an order, then the order has a Partially fulfilled fulfillment status.
'scheduled' => ['id' => 'scheduled', 'title' => 'Đã lên kế hoạch'],// 'Đã lên kế hoạch', // Prepaid subscription orders have a Scheduled status until the fulfillment date is reached
'onhold ' => ['id' => 'onhold', 'title' => 'Giữ hàng'],// 'Giữ hàng', // When upsell offers are presented to customers at checkout, the order fulfillment status is set to On hold temporarily. When a fulfillment is On hold you can reserve inventory for the order, but you can't fulfill the order until the fulfillment hold is released, and the order fulfillment status changes to Unfulfilled.
];
}

View File

@@ -0,0 +1,557 @@
<?php
namespace Hura8\Components\Order\Model;
use Hura8\Components\Order\Controller\OrderStatus;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\iSearch;
use Hura8\Interfaces\EntityType;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\Security\DataClean;
use Hura8\System\Security\DataType;
use Hura8\System\TimeManager;
use Hura8\System\Url;
class OrderModel extends aEntityBaseModel implements iEntityModel
{
protected $tb_order_detail = "tb_order_detail";
/* @var iSearch $objSearchModel */
protected $objSearchModel;
public function __construct() {
$this->objSearchModel = new OrderSearchModel();
parent::__construct(EntityType::ORDER, '', $this->objSearchModel);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getItems($orderId, $fields = "*") {
$query = $this->db->runQuery(
" SELECT ".$fields." FROM `".$this->tb_order_detail."` WHERE `order_id` = ? ",
['d'], [ $orderId ]);
$order_item = array();
foreach ( $this->db->fetchAll($query) as $info ) {
$item_info = ($info["item_info"]) ? \json_decode($info["item_info"], true) : false;
$in_cart = ($info["in_cart"]) ? \json_decode($info["in_cart"], true) : false;
$info['item_info'] = $item_info;
$info['in_cart'] = $in_cart;
$order_item[] = $info;
}
return $order_item;
}
//since we save the entire cart-structure for item_info, we need to re-construct it to make it easier to read and for various reporting/ email etc..
protected function buildOrderItemInfo($item_type, array $item_info) {
$new_info = [];
if($item_type == 'product') {
$new_info = System::stripProperty(array($item_info['info']), [
"variant_option",
'id',
'productId',
'priceUnit',
'price',
'currency',
'lastUpdate',
'warranty',
'productName',
'productUrl',
'productModel',
'productSummary',
'marketPrice',
'productImage',
'brand',
'visit',
'rating',
'reviewCount',
'quantity',
'productSKU',
'hasVAT',
'condition',
'specialOffer',
'specialOfferGroup',
'productType',
'thum_poster',
'promotion_price',
'addon'
])[0];
//find variants bought
if($new_info['config_count'] > 0) {
foreach ($item_info['in_cart'] as $_variant) {
$new_info['variants'][] = array(
"id" => $_variant['id'],
"sku" => $_variant['sku'],
"title" => $_variant['name'],
"image" => ($_variant['image']) ? $_variant['image'] : $item_info['info']['productImage']["medium"],
"url" => $item_info['info']['productUrl'],
"attribute" => $_variant['attribute'],
"price" => $_variant['price'],
"addon_total" => 0,
"currency" => $item_info['info']['currency'],
"priceUnit" => $item_info['info']['priceUnit'],
"quantity" => $_variant['quantity'],
"buyer_note" => $_variant['buyer_note'],
"addon" => [],
"promotion" => [
"list" => $item_info['info']['specialOffer'],
"group" => (isset($_variant['promotion'])) ? $this->buildPromotionFromGroup( $item_info['info']['specialOfferGroup'], $_variant['promotion']) : $this->buildPromotionFromGroup( $item_info['info']['specialOfferGroup'], array() ),
],
//..any other
);
}
}else{
//product does not have variant
$build_addon = [];
$total_addon_price = 0;
if(isset($item_info['info']['addon']) && isset($item_info['in_cart'][0]['addon'])) {
$build_addon = $this->getSelectedAddon($item_info['info']['addon'], $item_info['in_cart'][0]['addon']);
foreach ($build_addon as $addon) {
$total_addon_price += $addon['price'] * $addon['quantity'];
}
}
$variant_in_cart = (isset($item_info['in_cart'][0])) ? $item_info['in_cart'][0] : false;
$new_info['variants'][] = array(
"id" => 0,
"sku" => $item_info['info']['productSKU'],
"title" => $item_info['info']['productName'],
"image" => $item_info['info']['productImage']["medium"],
"url" => $item_info['info']['productUrl'],
"attribute" => [],
"price" => $item_info['info']['price'],
"addon_total" => $total_addon_price,
"currency" => $item_info['info']['currency'],
"priceUnit" => $item_info['info']['priceUnit'],
"quantity" => $variant_in_cart['quantity'],
"buyer_note" => $variant_in_cart['buyer_note'],
"addon" => $build_addon,
"promotion" => [
"list" => $item_info['info']['specialOffer'],
"group" => (isset($variant_in_cart['promotion'])) ? $this->buildPromotionFromGroup( $item_info['info']['specialOfferGroup'], $variant_in_cart['promotion']) : $this->buildPromotionFromGroup( $item_info['info']['specialOfferGroup'], array() ),
],
//..any other
);
}
//
return $new_info;
}
if($item_type == 'combo') {
$new_info = [
"price" => $item_info['info']['sale_price'],
"quantity" => $item_info['in_cart']['quantity'],
"buyer_note" => $item_info['in_cart']['buyer_note'],
"url" => '',
];
$new_info['product_list'] = System::stripProperty($item_info['info']['product_list'], [
"variant_option",
'id',
'productId',
'priceUnit',
'price',
'currency',
'lastUpdate',
'warranty',
//'productName',
//'productUrl',
//'productModel',
'productSummary',
'marketPrice',
'productImage',
'brand',
'visit',
'rating',
'reviewCount',
'config_count',
'quantity',
//'productSKU',
'hasVAT',
'condition',
'specialOffer',
'specialOfferGroup',
'productType',
'thum_poster',
'promotion_price',
'addon',
'imageCollection',
'variants'
]);
return $new_info;
}
if($item_type == 'deal') {
$new_info = [
"price" => $item_info['info']['price'],
"quantity" => $item_info['in_cart']['quantity'],
"buyer_note" => $item_info['in_cart']['buyer_note'],
"url" => '/deal/'.$item_info['info']['id'],
];
return $new_info;
}
return $new_info;
}
protected function getSelectedAddon($all_product_addons, $in_cart) {
if(!is_array($all_product_addons) || !is_array($in_cart)) return [];
$result = [];
foreach ($all_product_addons as $item) {
foreach ($in_cart as $selected) {
if($item['addon_id'] == $selected['id']) {
$result[] = [
"id" => $selected['id'],
"title" => $item['title'],
"price" => $item['price'],
"quantity" => (isset($selected['quantity'])) ? intval($selected['quantity']) : 1,
"related_article_url" => $item['related_article_url'],
];
}
}
}
return $result;
}
protected function buildPromotionFromGroup( $offer_group, $selected_one_promotion) {
if(!is_array($offer_group) && !is_array($selected_one_promotion)) return false;
$promotion = [];
foreach ($offer_group as $group) {
if($group['type'] == 'one') {
$selected_promotion = [];
foreach ($group['promotion'] as $promo) {
foreach ($selected_one_promotion as $selected) {
if($selected['promotion_id'] == $promo['id'] && $selected['group_id'] == $group['id']) {
$selected_promotion[] = $promo;
}
}
}
$promotion[] = [
"id" => $group['id'],
"title" => $group['title'],
"note" => $group['note'],
"promotion" => $selected_promotion,
];
}else{
$promotion[] = [
"id" => $group['id'],
"title" => $group['title'],
"note" => $group['note'],
"promotion" => $group['promotion'],
];
}
}
return $promotion;
}
protected function getListFilterCondition($list_id) {
if($list_id == 'mine') {
return [
'assign_to' => ADMIN_ID,
];
}
if($list_id == 'new') {
return [
'status' => 'new',
];
}
if($list_id == 'unpaid') {
return [
'payment' => 'unpaid',
];
}
if($list_id == 'partially-paid') {
return [
'payment' => 'partially-paid',
];
}
if($list_id == 'unfulfilled') {
return [
'fullfillment' => 'unfulfilled',
];
}
if($list_id == 'partially-fulfilled') {
return [
'fullfillment' => 'partially-fulfilled',
];
}
return null;
}
public function getStatusHistory($orderId){
$query = $this->db->runQuery("
select * from ".TB_ORDER_STATUS_HISTORY."
WHERE order_id = ?
order by id desc
limit 100
", ['d'], [ $orderId ] ) ;
return $this->db->fetchAll($query);
}
public function getItemsForOrderList(array $list_ids, $fields = "*") {
if(!sizeof($list_ids)) return [];
list($parameterized_ids, $bind_types) = create_bind_sql_parameter_from_value_list($list_ids, 'int');
$query = $this->db->runQuery("
SELECT ".$fields." FROM `".$this->tb_order_detail."`
WHERE `order_id` IN (".$parameterized_ids.")
", $bind_types, $list_ids);
$result = array();
foreach ( $this->db->fetchAll($query) as $rs ) {
$item_info = unserialize($rs['item_info']);
if( isset($item_info["info"]) && isset($item_info["in_cart"])) {
$rs['item_info'] = $this->buildOrderItemInfo($rs['item_type'], $item_info );
} else {
$rs['item_info'] = null;
}
$result[$rs['order_id']][] = $rs;
}
return $result;
}
protected function _buildQueryConditionExtend(array $conditions) : ?array
{
$catCondition = [];
$bind_types = [];
$bind_values = [];
/*
$conditions = array(
'orderCode' => '',
'query' => '',
'coupon' => '',
'cus_id' => '',
'province' => '',
'district' => '',
'folder' => '',
'view_status' => '',
'update_by' => '',
'shipping_status' => '',
'assign_to' => '',
'from_date' => '',
'to_date' => '',
'from_hour' => '',
'to_hour' => '',
'excluded_ids' => '',
'included_ids' => '',
'payment' => '',
'fullfillment' => '',
'list' => '',
);*/
// merge with special list
if(isset($conditions['list']) && $conditions['list']) {
$list_condition = $this->getListFilterCondition($conditions['list']);
if($list_condition) {
// update and over-write any key in $conditions if exist
foreach ($list_condition as $key => $value) {
$conditions[$key] = $value;
}
}
}
if(isset($conditions['orderCode']) && $conditions['orderCode']) {
$orderCode = DataClean::makeInputSafe($conditions['orderCode'],DataType::INTEGER);
$catCondition[] = " AND `orderId` LIKE '".$orderCode."%' ";
}
if(isset($conditions['coupon']) && $conditions['coupon']) {
$coupon = preg_replace("/[^a-z0-9_\-\.]/i","", $conditions['coupon']);
if($coupon) $catCondition[] = " AND ( LENGTH(`order_discount`) > 10 AND `order_discount` LIKE '%".$coupon."%' ) ";
}
if(isset($conditions['cus_id']) && $conditions['cus_id']) {
$catCondition[] = " AND buyerId = '".intval($conditions['cus_id'])."'";
}
if(isset($conditions['province']) && $conditions['province']) {
$catCondition[] = " AND province = '".intval($conditions['province'])."'";
}
if(isset($conditions['district']) && $conditions['district']) {
$catCondition[] = " AND district = '".intval($conditions['district'])."'";
}
if(isset($conditions['folder']) && $conditions['folder']) {
$catCondition[] = " AND `folder` = '". preg_replace("/[^a-z0-9_\-\.]/i", "", $conditions['folder'])."' ";
}
if(isset($conditions['payment']) && array_key_exists($conditions['payment'], OrderStatus::PAYMENT_STATUS ) ) {
$catCondition[] = " AND `payment_status` = '". $this->db->escape($conditions['payment'])."' ";
}
if(isset($conditions['fullfillment']) && array_key_exists($conditions['fullfillment'], OrderStatus::FULFILLMENT_STATUS) ) {
$catCondition[] = " AND `fulfillment_status` = '". $this->db->escape($conditions['fullfillment'])."' ";
}
if(isset($conditions['status']) && array_key_exists($conditions['status'], OrderStatus::ORDER_STATUS) ) {
$catCondition[] = " AND `order_status` = '". $this->db->escape($conditions['status'])."' ";
}
if(isset($conditions['view_status']) && $conditions['view_status']) {
if($conditions['view_status'] == 'no-status') {
$catCondition[] = " AND `status_id` = 0 ";
} else {
$catCondition[] = " AND `status_id` = '". intval($conditions['view_status'])."' ";
}
}
if(isset($conditions['update_by']) && $conditions['update_by']) {
$catCondition[] = " AND `status_update_by` = '". $this->db->escape($conditions['update_by'])."' ";
}
if(isset($conditions['shipping_status']) && $conditions['shipping_status']) {
$catCondition[] = " AND `admin_shipping_status` = '". preg_replace("/[^a-z0-9_\-\.]/i", "", $conditions['shipping_status'])."' ";
}
if(isset($conditions['assign_to']) && $conditions['assign_to']) {
$catCondition[] = " AND `assign_to` = '". intval($conditions['assign_to'])."' ";
}
//filter by date
if(isset($conditions['from_date']) && $conditions['from_date']) {
$catCondition[] = " AND order_time >= '". intval(strtotime(TimeManager::convert_date_from_javascript($conditions['from_date'])." 00:00"))."' ";
}
if(isset($conditions['to_date']) && $conditions['to_date']) {
$catCondition[] = " AND order_time <= '".strtotime(TimeManager::convert_date_from_javascript($conditions['to_date'])." 23:59")."' ";
}
//filter by hour
if(isset($conditions['from_hour']) && $conditions['from_hour']) {
$catCondition[] = " AND `order_hour` >= '".intval($conditions['from_hour'])."' ";
}
if(isset($conditions['to_hour']) && $conditions['to_hour']) {
$catCondition[] = " AND `order_hour` <= '".intval($conditions['to_hour'])."' ";
}
if(isset($conditions["excluded_ids"]) && $conditions["excluded_ids"] ){
$list_ids = filterNumber(explode(",", $conditions["excluded_ids"]));
if(sizeof($list_ids)) $catCondition[] = " AND `orderId` NOT IN (".join(',', $list_ids ).") ";
}
if(isset($conditions["included_ids"]) && $conditions["included_ids"] ){
$list_ids = filterNumber(explode(",", $conditions["included_ids"]));
if(sizeof($list_ids)) $catCondition[] = " AND `orderId` IN (".join(',', $list_ids ).") ";
}
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
protected function beforeCreateItem(array $input_info) : AppResponse
{
$info = $input_info;
if(isset($info['file_external_url'])) {
if($info['file_external_url'] && !Url::isUrlValid($info['file_external_url'])) {
$info['file_external_url'] = '';
}
}
$info['create_time'] = CURRENT_TIME;
$info['create_by'] = ADMIN_NAME;
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function beforeUpdateItem($item_id, $current_item_info, $new_input_info) : AppResponse
{
$info = $new_input_info;
unset($info['id']);
if(isset($info['file_external_url']) && $info['file_external_url'] && !Url::isUrlValid($info['file_external_url'])) {
$info['file_external_url'] = '';
}
$info['last_update'] = CURRENT_TIME;
$info['last_update_by'] = ADMIN_NAME;
return new AppResponse('ok', null, $info);
}
protected function beforeDeleteItem($item_id, $item_info) : AppResponse
{
return new AppResponse('ok');
}
protected function afterDeleteItem($item_id, $item_info)
{
}
protected function afterCreateItem($new_item_id, $new_item_info)
{
}
protected function afterUpdateItem($item_id, $old_item_info, $new_item_info)
{
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Hura8\Components\Order\Model;
use Hura8\Interfaces\iSearch;
use Hura8\System\Model\aSearchBaseModel;
class OrderSearchModel extends aSearchBaseModel implements iSearch
{
private $filter_fields = [
"folder" => "tb_order.price",
];
private $fulltext_fields = [
"keywords" => ["tb_order.buyerName", ],
];
public function __construct()
{
parent::__construct(
"tb_order",
$this->fulltext_fields,
$this->filter_fields
);
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Hura8\Components\Order\Model;
use Hura8\Database\iConnectDB;
class OrderStatusModel
{
/* @var iConnectDB $db */
protected $db;
protected $tb_status = 'tb_order_status';
protected $tb_status_history = 'tb_order_status_history';
public function __construct() {
$this->db = get_db('', ENABLE_DB_DEBUG);
}
public function getStatusHistory($orderId){
$query = $this->db->runQuery("
select * from ".$this->tb_status_history."
WHERE order_id = ?
order by id desc
limit 100
", ['d'], [ $orderId ] ) ;
return $this->db->fetchAll($query);
}
public function createHistory($order_id, $status_type = 'order', $system_status = '', $comment = '', array $data= [], array $other_info = []) {
$info = $other_info;
$info['order_id'] = $order_id;
$info['status_type'] = $status_type;
$info['system_status'] = $system_status;
$info['data'] = \json_encode($data);
$info['comment'] = substr($comment, 0, 150);
$info['create_time'] = CURRENT_TIME;
$info['create_by'] = (defined('ADMIN_NAME')) ? ADMIN_NAME : '';
return $this->db->insert($this->tb_status_history, $info);
}
public function getCustomerCancelStatusId() {
$info = $this->db->select($this->tb_status, ['id'], [
"system_status" => ["=", "cancel"],
'message' => 'Khách hàng hủy',
], '', 1);
if($info) {
return $info['id'];
}
// create and return
return $this->create([
'message' => 'Khách hàng hủy',
'system_status' => "cancel",
'create_time' => CURRENT_TIME,
'create_by' => 'System',
]);
}
public function getInfo($status_id) {
return $this->db->getItemInfo($this->tb_status, $status_id, 'id');
}
public function getAll() {
$query = $this->db->runQuery(" select * from ".$this->tb_status." order by `id` desc ") ;
$item_list = array();
foreach ( $this->db->fetchAll($query) as $rs ) {
if(!$rs['system_status']) $rs['system_status'] = 'new';
//$rs['system_order_status'] = System::$ORDER_STATUS[$rs['system_status']];
$item_list[] = $rs;
}
return $item_list;
}
public function delete($id) {
return $this->db->runQuery("DELETE FROM ".$this->tb_status." WHERE `id` = ? limit 1 ", ['d'], [ $id ]) ;
}
public function update($id, array $info) {
return $this->db->update($this->tb_status, $info, ['id' => $id]);
}
public function create(array $info) {
return $this->db->insert($this->tb_status, $info);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Hura8\Components\Page\AdminController;
use Hura8\Components\Page\Controller\bPageController;
use Hura8\Interfaces\iEntityAdminController;
use Hura8\Traits\AdminEntityBaseControllerTraits;
class APageController extends bPageController implements iEntityAdminController
{
use AdminEntityBaseControllerTraits;
public function updateTableInfo($item_id, array $new_item_info) {
return $this->objPageModel->updateTableInfo($item_id, $new_item_info);
}
protected function deleteFileBeforeDeleteItem($item_id): bool
{
// delete thumb files
$item_info = $this->getInfo($item_id);
$this->deleteThumbnailFile($item_info['thumbnail']);
//delete media files?
// todo:
return true;
}
protected function deleteThumbnailFile($file_name): bool
{
if(!$file_name) {
return false;
}
foreach (self::$resized_sizes as $size => $value) {
$file_local_path = PUBLIC_DIR . "/". self::$image_folder . "/". $size. IMAGE_FILE_SEPARATOR . $file_name;
unlink($file_local_path);
}
// remove original file
$file_local_path = PUBLIC_DIR . "/". self::$image_folder . "/". $file_name;
return unlink($file_local_path);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Hura8\Components\Page\Controller;
use Hura8\Components\Page\Model\PageLanguageModel;
use Hura8\Components\Page\Model\PageModel;
use Hura8\System\Controller\aEntityBaseController;
class bPageController extends aEntityBaseController
{
static $image_folder = "media/static";
static $resized_sizes = array(
't' => ['width' => 200,] ,
'l' => ['width' => 600,] ,
);
protected $objPageModel;
public function __construct()
{
$this->objPageModel = new PageModel();
if(!$this->isDefaultLanguage()) {
//$this->objPageLanguageModel->createTableLang();
parent::__construct($this->objPageModel, new PageLanguageModel());
}else{
parent::__construct($this->objPageModel);
}
}
// get full info- basic with description
public function getFullInfo($id) : ?array
{
if(!$id) return null;
return self::getCache("getFullInfo-".$id."-".$this->view_language, function () use ($id){
$info = $this->objPageModel->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 $this->formatItemInfo($info);
});
}
protected function formatItemInfo(array $item_info)
{
if(!$item_info) return null;
$info = $item_info;
$info['image'] = self::getResizedImageCollection($info['thumbnail']);
return $info;
}
protected function formatItemInList(array $item_info)
{
return $this->formatItemInfo($item_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 : '';
}
return $image;
}
}

View File

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

View File

@@ -0,0 +1,76 @@
<?php
namespace Hura8\Components\Page\Model;
use Hura8\Interfaces\AppResponse;
use Hura8\Interfaces\iEntityModel;
use Hura8\Interfaces\EntityType;
use Hura8\System\Controller\UrlManagerController;
use Hura8\System\Model\aEntityBaseModel;
use Hura8\System\ModuleManager;
class PageModel extends aEntityBaseModel implements iEntityModel
{
protected $richtext_fields = [
'content',
'content_html',
];
protected $tb_page_info = "tb_page_info";
public function __construct() {
parent::__construct(EntityType::PAGE, '', null, $this->richtext_fields);
}
protected function extendedFilterOptions() : array
{
return [
// empty for now
];
}
public function getFullInfo($id) : ?array
{
$query = $this->db->runQuery(
"SELECT * FROM `".$this->tb_entity."` basic, `".$this->tb_page_info."` info
WHERE basic.`id` = info.`page_id` AND basic.id = ?
LIMIT 1 ",
['d'], [$id]
);
if( $item_info = $this->db->fetchAssoc($query)){
return $item_info;
}
return null;
}
protected function _buildQueryConditionExtend(array $filter_condition): ?array
{
/*$condition = array(
"q" => "",
"status" => 0,
);*/
/*$condition = array(
"letter" => "",
);*/
$catCondition = [];
$bind_types = [];
$bind_values = [];
/*if(isset($filter_condition["letter"]) && strlen($filter_condition["letter"]) == 1){
$catCondition[] = " AND `letter` = ? ";
$bind_types[] = 's';
$bind_values[] = $filter_condition["letter"];
}*/
return array( join(" ", $catCondition), $bind_types, $bind_values);
}
}

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,18 @@
<?php
namespace Hura8\Components\Product\AdminController;
use Hura8\Components\Product\Controller\bProductCategoryController;
use Hura8\Traits\AdminEntityCategoryControllerTraits;
class AProductCategoryController extends bProductCategoryController
{
use AdminEntityCategoryControllerTraits;
public function getAttributeList($catId) {
return $this->objProductCategoryModel->getAttributeList($catId);
}
}

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,83 @@
<?php
namespace Hura8\Components\Product\Model;
use Hura8\Interfaces\iEntityCategoryModel;
use Hura8\System\Model\aCategoryBaseModel;
use Hura8\Interfaces\EntityType;
class ProductCategoryModel extends aCategoryBaseModel implements iEntityCategoryModel
{
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;
}
}

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