update
7
.htaccess
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
RewriteEngine on
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_URI} !^/index.php
|
||||||
|
# prevent rewrite non-existent files
|
||||||
|
RewriteCond %{REQUEST_URI} !\.(jpg|png|gif|css|js|php|tiff|jpeg|ico)$
|
||||||
|
RewriteRule ^(.*)$ /index.php [QSA,L]
|
||||||
74
README.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<h1>Hướng dẫn</h1>
|
||||||
|
|
||||||
|
<p>Link thiết kế: <a href="https://www.figma.com/design/qqipE1AsUMl31zk6wezduD/X-Store?node-id=110-11&t=eJARcn1sDXYeb031-0" target="_blank"> <strong>Giao diện Xstore</strong> </a></p>
|
||||||
|
|
||||||
|
<p>Repo: <a href="https://repo.hurasoft.com/tieptk/xstore" target="_blank">https://repo.hurasoft.com/tieptk/xstore</a></p>
|
||||||
|
|
||||||
|
<p>Theo dõi tiến độ: <a href="https://docs.google.com/spreadsheets/d/1Po3ANsG00pm_Y3dnrwuV81cidTCCqepYmR3yPDlIq6c/edit#gid=0" target="_blank">https://docs.google.com/spreadsheets/d/1Po3ANsG00pm_Y3dnrwuV81cidTCCqepYmR3yPDlIq6c/edit#gid=0</a></p>
|
||||||
|
|
||||||
|
<h2>Cài đặt hệ thống</h2>
|
||||||
|
|
||||||
|
<p>Test và làm việc chính tại web: <a href="http://local.hura8_admin/"> http://local.xstore/</a> </p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Tải phần mềm XAMPP tại <a href="https://www.apachefriends.org/download.html" target="_blank">https://www.apachefriends.org/download.html</a> để chạy PHP</li>
|
||||||
|
<li>Chỉnh file hosts trong máy tính C:\Windows\System32\drivers\etc\hosts:
|
||||||
|
<pre><code>
|
||||||
|
127.0.0.1 local.xstore/
|
||||||
|
</code></pre>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Cài đặt ../xampp/apache/conf/extra/httpd-vhosts.conf của apache trong XAMPP
|
||||||
|
<pre><code>
|
||||||
|
<VirtualHost *:80>
|
||||||
|
DocumentRoot "/thuc-muc-check-out/xstore"
|
||||||
|
ServerName local.hura8_admin
|
||||||
|
<Directory "/thuc-muc-check-out/xstore/">
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
|
</code></pre>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Cài đặt ../xampp/apache/conf/httpd.conf của apache trong XAMPP
|
||||||
|
<pre><code>
|
||||||
|
# AllowOverride controls what directives may be placed in .htaccess files.
|
||||||
|
# It can be "All", "None", or any combination of the keywords:
|
||||||
|
# AllowOverride FileInfo AuthConfig Limit
|
||||||
|
AllowOverride All
|
||||||
|
</code></pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Cấu trúc thư mục</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>/template: các file template html chia theo module/view</li>
|
||||||
|
<li>/data: cung cấp dữ liệu cho template và hiển thị dữ liệu trên template qua code Liquid <a href="https://shopify.github.io/liquid/" target="_blank">https://shopify.github.io/liquid/</a></li>
|
||||||
|
<li>/assets: lưu các file ảnh/css/js dùng cho giao diện</li>
|
||||||
|
<li>/package: thư viện code PHP hỗ trợ</li>
|
||||||
|
<li>/inc: các code PHP hỗ trợ</li>
|
||||||
|
<li>index.php: file gốc để truy cập nội dung</li>
|
||||||
|
<li>ajax.php: file gốc để gọi ajax</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Cài đặt Composer (dùng tải package của PHP)</h2>
|
||||||
|
|
||||||
|
<p>Xem hướng dẫn: <a href="https://getcomposer.org/doc/00-intro.md#installation-windows" target="_blank">https://getcomposer.org/doc/00-intro.md#installation-windows</a> </p>
|
||||||
|
|
||||||
|
<p>Sau khi cài đặt xong. Mở cmd của Windows và thao tác lệnh sau để cài các thư viện code PHP cần cho dự án này.</p>
|
||||||
|
|
||||||
|
<pre><code>
|
||||||
|
> cd /thuc-muc-check-out/xstore/package
|
||||||
|
> composer i
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<h2> Sử dụng <u>Tailwind</u> để style giao diện.</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>[Dùng chính] <a href="https://daisyui.com/components/" target="_blank">https://daisyui.com/components/</a></li>
|
||||||
|
<li><a href="https://tailwindui.com/" target="_blank"> https://tailwindui.com/ </a></li>
|
||||||
|
<li><a href="https://tailblocks.cc/" target="_blank"> https://tailblocks.cc/ </a></li>
|
||||||
|
<li><a href="https://www.hyperui.dev/" target="_blank"> https://www.hyperui.dev/ </a></li>
|
||||||
|
<li><a href="https://flowbite.com/docs/plugins/charts/" target="_blank"> Biểu đồ </a></li>
|
||||||
|
</ul>
|
||||||
17
_shared.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
const ROOT_DIR = __DIR__;
|
||||||
|
const CONFIG_DIR = ROOT_DIR . '/inc/config';
|
||||||
|
|
||||||
|
const IMAGE_FILE_SEPARATOR = "-";
|
||||||
|
define("CURRENT_TIME", time());
|
||||||
|
const STATIC_DOMAIN = "http://hura8.hurasoft.com";
|
||||||
|
const ENABLE_DB_DEBUG = true;
|
||||||
|
const LANGUAGE = 'vi';
|
||||||
|
const IS_DEFAULT_LANGUAGE = true;
|
||||||
|
|
||||||
|
include ROOT_DIR."/inc/common.php";
|
||||||
|
include ROOT_DIR."/inc/fun.db.php";
|
||||||
|
|
||||||
|
// start autoload
|
||||||
|
init_autoload();
|
||||||
7
ajax.php
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
include __DIR__."/_shared.php";
|
||||||
BIN
assets/images/background-banner.jpg
Normal file
|
After Width: | Height: | Size: 252 KiB |
BIN
assets/images/background-login.jpg
Normal file
|
After Width: | Height: | Size: 429 KiB |
BIN
assets/images/background-login.png
Normal file
|
After Width: | Height: | Size: 842 KiB |
BIN
assets/images/background-pricing.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
assets/images/background-tamnhin.jpg
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/images/box-fontend.png
Normal file
|
After Width: | Height: | Size: 694 KiB |
BIN
assets/images/congcuseo.png
Normal file
|
After Width: | Height: | Size: 503 KiB |
BIN
assets/images/fontend-hacom.jpg
Normal file
|
After Width: | Height: | Size: 428 KiB |
BIN
assets/images/fontend-mialala.png
Normal file
|
After Width: | Height: | Size: 448 KiB |
BIN
assets/images/fontend-thanhtoan.jpg
Normal file
|
After Width: | Height: | Size: 284 KiB |
BIN
assets/images/frontend-traphaco.png
Normal file
|
After Width: | Height: | Size: 441 KiB |
BIN
assets/images/icon_2025.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
assets/images/imag-trial.png
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
assets/images/image-article.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/images/image-bigsale.jpg
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
assets/images/image-chuyendoi.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
assets/images/image-create.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
assets/images/image-credit.jpg
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
assets/images/image-giamchiphi.png
Normal file
|
After Width: | Height: | Size: 438 KiB |
BIN
assets/images/image-hieubietsansac.png
Normal file
|
After Width: | Height: | Size: 364 KiB |
BIN
assets/images/image-kethop.png
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
assets/images/image-khoinghiep.png
Normal file
|
After Width: | Height: | Size: 482 KiB |
BIN
assets/images/image-platforms.png
Normal file
|
After Width: | Height: | Size: 282 KiB |
BIN
assets/images/image-quangcao.png
Normal file
|
After Width: | Height: | Size: 597 KiB |
BIN
assets/images/image-sell.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
assets/images/image-vanhanh.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
assets/images/image-x2doanthu.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
assets/images/img-chuyendoi.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
assets/images/imge-thuonghieu.jpg
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
assets/images/keotha.png
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
assets/images/logo-chatngay.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/images/logo-footer.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/logo-hura8.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/images/logo-xstore-white.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/images/logo.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/images/mygear.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
assets/images/phattienlinhhoat.png
Normal file
|
After Width: | Height: | Size: 416 KiB |
BIN
assets/images/themdoanhthu.png
Normal file
|
After Width: | Height: | Size: 598 KiB |
BIN
assets/images/xaydungthuonghieu.png
Normal file
|
After Width: | Height: | Size: 546 KiB |
314
assets/js/main.js
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
// menu
|
||||||
|
ShowMenu();
|
||||||
|
|
||||||
|
// click tab
|
||||||
|
enableSmoothScroll("#tab a", 800);
|
||||||
|
|
||||||
|
btnPricing();
|
||||||
|
|
||||||
|
|
||||||
|
showInput();
|
||||||
|
|
||||||
|
showSelect();
|
||||||
|
})
|
||||||
|
|
||||||
|
function ShowMenu() {
|
||||||
|
$menu = $('#js-show-menu .item-menu a');
|
||||||
|
$box_menu = $('#box-menu');
|
||||||
|
|
||||||
|
$menu.on('click', function () {
|
||||||
|
$data_menu = $(this).attr('data-menu');
|
||||||
|
$('.content-menu').addClass('hidden')
|
||||||
|
$('#' + $data_menu).removeClass('hidden');
|
||||||
|
if ($(this).hasClass('active')) {
|
||||||
|
$(this).removeClass('active');
|
||||||
|
$('#background-opacity').addClass('hidden');
|
||||||
|
$('#box-menu').addClass('translate-y-[-100%]');
|
||||||
|
$(this).find($('i')).removeClass('fa-sort-up mt-[10px]');
|
||||||
|
} else {
|
||||||
|
$(this).addClass('active');
|
||||||
|
$('#background-opacity').removeClass('hidden');
|
||||||
|
$('#box-menu').removeClass('translate-y-[-100%]');
|
||||||
|
$(this).find($('i')).addClass('fa-sort-up mt-[10px]');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeBackgorund() {
|
||||||
|
$('#background-opacity').addClass('hidden');
|
||||||
|
$('#box-menu').addClass('translate-y-[-100%]');
|
||||||
|
$('#js-show-menu .item-menu').find($('i')).removeClass('fa-sort-up mt-[10px]');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFaq(el) {
|
||||||
|
var $el = $(el);
|
||||||
|
var $content = $el.next(".content-faq");
|
||||||
|
var $icon = $el.find("i");
|
||||||
|
|
||||||
|
if ($content.hasClass("open")) {
|
||||||
|
// Đóng
|
||||||
|
$content.removeClass("open").css("max-height", 0);
|
||||||
|
$icon.removeClass("rotate-180");
|
||||||
|
} else {
|
||||||
|
// Đóng các FAQ khác (nếu muốn accordion)
|
||||||
|
$(".content-faq").removeClass("open").css("max-height", 0);
|
||||||
|
$(".title i").removeClass("rotate-180");
|
||||||
|
|
||||||
|
// Mở
|
||||||
|
$content.addClass("open").css("max-height", $content.prop("scrollHeight") + "px");
|
||||||
|
$icon.addClass("rotate-180");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function enableSmoothScroll(selector, speed) {
|
||||||
|
$(selector).click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var target = $($(this).attr("href"));
|
||||||
|
if (target.length) {
|
||||||
|
$("html, body").animate(
|
||||||
|
{ scrollTop: target.offset().top - 100 },
|
||||||
|
speed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function btnPricing() {
|
||||||
|
$('#billingToggle').on('change', function () {
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
// Annually
|
||||||
|
$('#labelMonthly').removeClass('text-black').addClass('text-gray-500');
|
||||||
|
$('#labelAnnually').removeClass('text-gray-500').addClass('text-black');
|
||||||
|
$('#toggleBox').removeClass('border-gray-300').addClass('border-black')
|
||||||
|
.addClass('after:translate-x-6');
|
||||||
|
} else {
|
||||||
|
// Monthly
|
||||||
|
$('#labelAnnually').removeClass('text-black').addClass('text-gray-500');
|
||||||
|
$('#labelMonthly').removeClass('text-gray-500').addClass('text-black');
|
||||||
|
$('#toggleBox').removeClass('border-black').addClass('border-gray-300')
|
||||||
|
.removeClass('after:translate-x-6');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function showCompare() {
|
||||||
|
$('.item-compare i').toggleClass('fa-plus fa-minus');
|
||||||
|
$('.content-compare').toggleClass('hidden mt-[15px] pt-[15px]');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function showInput() {
|
||||||
|
$('.form-input input').on('focus', function () {
|
||||||
|
$lable = $(this).closest($('.form-input')).find($('label'));
|
||||||
|
$error = $(this).closest($('.check-form')).find('.note-error');
|
||||||
|
$lable.toggleClass('top-[-1.75rem] text-[15px] text-[12px]');
|
||||||
|
$error.html('');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSelect() {
|
||||||
|
$('.form-select').on('click', function () {
|
||||||
|
$(this).find($('label')).removeClass('text-[15px] top-[2px]')
|
||||||
|
$(this).find($('label')).addClass('top-[-1.75rem] text-[12px]');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkInputName() {
|
||||||
|
var error = false;
|
||||||
|
var check_name = document.getElementById('first_name').value;
|
||||||
|
var $name = $('#first_name');
|
||||||
|
var item_name = $name.parents(".check-form");
|
||||||
|
if (check_name.length < 4) {
|
||||||
|
item_name.find($('.note-error')).html("Tên quá ngắn");
|
||||||
|
error = true;
|
||||||
|
} else if (check_name.indexOf('<script') > -1) {
|
||||||
|
item_name.find($('.note-error')).html("Họ tên chứa các ký tự không hợp lệ, bạn vui lòng kiểm tra lại");
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
item_name.find($('.note-error')).html("");
|
||||||
|
}
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkInputPhone() {
|
||||||
|
var number_regex1 = /^[0]\d{9}$/i;
|
||||||
|
var number_regex2 = /^[0]\d{10}$/i;
|
||||||
|
|
||||||
|
var check_tel = document.getElementById('phone').value;
|
||||||
|
var $tel = $("#phone");
|
||||||
|
var item_tel = $tel.parents(".check-form");
|
||||||
|
if (check_tel.length < 4) {
|
||||||
|
item_tel.addClass('error')
|
||||||
|
item_tel.find($('.note-error')).html("Bạn chưa nhập SĐT");
|
||||||
|
error = true;
|
||||||
|
} else if (!check_tel.match(number_regex1) && !check_tel.match(number_regex2)) {
|
||||||
|
item_tel.addClass('error')
|
||||||
|
item_tel.find($('.note-error')).html("Số điện thoại chưa chính xác");
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
item_tel.removeClass('error');
|
||||||
|
item_tel.find($('.note-error')).html("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkInputEmail() {
|
||||||
|
var check_email = document.getElementById('email').value;
|
||||||
|
var $email = $("#email");
|
||||||
|
var item_email = $email.parents(".check-form");
|
||||||
|
if (check_email.length < 4) {
|
||||||
|
item_email.addClass('error')
|
||||||
|
item_email.find($('.note-error')).html("Bạn chưa nhập Email");
|
||||||
|
error = true;
|
||||||
|
} else if (!validateEmail(check_email)) {
|
||||||
|
item_email.addClass('error')
|
||||||
|
item_email.find($('.note-error')).html("Địa chỉ email chưa chính xác");
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
item_email.removeClass('error');
|
||||||
|
item_email.find($('.note-error')).html("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkProvince() {
|
||||||
|
var check_province = document.getElementById('province').value;
|
||||||
|
var $check_province = $('#province');
|
||||||
|
var item_province = $check_province.parents('.check-form');
|
||||||
|
if (check_province == 0) {
|
||||||
|
item_province.addClass('error');
|
||||||
|
item_province.find($('.note-error')).html("Bạn chưa chọn Tỉnh/Thành phố");
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
item_province.removeClass('error');
|
||||||
|
item_province.find($('.note-error')).html("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkInputShop() {
|
||||||
|
var error = false;
|
||||||
|
var check_shop = document.getElementById('shop').value;
|
||||||
|
var $shop = $('#shop');
|
||||||
|
var item_shop = $shop.parents(".check-form");
|
||||||
|
if (check_shop.length < 4) {
|
||||||
|
item_shop.find($('.note-error')).html("Tên quá ngắn");
|
||||||
|
error = true;
|
||||||
|
} else if (check_shop.indexOf('<script') > -1) {
|
||||||
|
item_shop.find($('.note-error')).html("Tên chứa các ký tự không hợp lệ, bạn vui lòng kiểm tra lại");
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
item_shop.find($('.note-error')).html("");
|
||||||
|
}
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkformTrial() {
|
||||||
|
|
||||||
|
var error = false;
|
||||||
|
|
||||||
|
checkInputName();
|
||||||
|
|
||||||
|
checkInputPhone();
|
||||||
|
|
||||||
|
checkInputEmail();
|
||||||
|
|
||||||
|
checkInputShop();
|
||||||
|
|
||||||
|
checkProvince();
|
||||||
|
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
alert('Vui lòng kiểm tra lại thông tin');
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$(".button-send").css("pointer-events", "none");
|
||||||
|
$(".button-send").html("ĐANG XỬ LÝ...");
|
||||||
|
localStorage.getItem("Key_voucher");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function validateEmail(sEmail) {
|
||||||
|
var filter = /^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
|
||||||
|
if (filter.test(sEmail)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initPasswordStrength() {
|
||||||
|
const inputPassword = document.getElementById('password');
|
||||||
|
const toggleBtn = document.getElementById('togglePassword');
|
||||||
|
const strengthBar = document.getElementById('strengthBar');
|
||||||
|
const strengthText = document.getElementById('strengthText');
|
||||||
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
|
||||||
|
// Đánh giá mật khẩu
|
||||||
|
function evaluateStrength(password) {
|
||||||
|
let score = 0;
|
||||||
|
if (password.length >= 8) score++;
|
||||||
|
if (/[A-Z]/.test(password)) score++;
|
||||||
|
if (/\d/.test(password)) score++;
|
||||||
|
if (/[^A-Za-z0-9]/.test(password)) score++;
|
||||||
|
return score; // 0..4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render UI
|
||||||
|
function renderStrength(score) {
|
||||||
|
let label = "—", color = "bg-gray-300", width = "0%";
|
||||||
|
if (score === 1) { label = "yếu"; color = "bg-red-500"; width = "25%"; }
|
||||||
|
if (score === 2) { label = "trung bình"; color = "bg-yellow-500"; width = "50%"; }
|
||||||
|
if (score === 3) { label = "khá"; color = "bg-lime-500"; width = "75%"; }
|
||||||
|
if (score === 4) { label = "mạnh"; color = "bg-green-500"; width = "100%"; }
|
||||||
|
|
||||||
|
const levels = [
|
||||||
|
{ label: "—", color: "bg-gray-300", width: "0%" },
|
||||||
|
{ label: "yếu", color: "bg-red-500", width: "25%" },
|
||||||
|
{ label: "trung bình", color: "bg-yellow-500", width: "50%" },
|
||||||
|
{ label: "khá", color: "bg-lime-500", width: "75%" },
|
||||||
|
{ label: "mạnh", color: "bg-green-500", width: "100%" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const level = levels[score];
|
||||||
|
strengthBar.className = "h-1 rounded transition-all " + level.color;
|
||||||
|
strengthBar.style.width = width;
|
||||||
|
strengthText.textContent = "Độ mạnh của mật khẩu: " + level.label;
|
||||||
|
submitBtn.disabled = score < 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event nhập mật khẩu
|
||||||
|
inputPassword.addEventListener('input', e => {
|
||||||
|
$('#check-pass').removeClass('hidden');
|
||||||
|
const score = evaluateStrength(e.target.value);
|
||||||
|
renderStrength(score);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle hiển thị/ẩn mật khẩu
|
||||||
|
toggleBtn.addEventListener('click', () => {
|
||||||
|
inputPassword.type = inputPassword.type === 'password' ? 'text' : 'password';
|
||||||
|
});
|
||||||
|
|
||||||
|
renderStrength(0); // khởi tạo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
initPasswordStrength();
|
||||||
202
assets/script/style.css
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
:root {
|
||||||
|
--color-blue: #0f5edd;
|
||||||
|
--text-green: #00c75d;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #00112b;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(0.7, #00112b), color-stop(0.5, transparent), to(transparent));
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #fcfcfc;
|
||||||
|
max-width: 1920px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix::after {
|
||||||
|
content: "";
|
||||||
|
clear: both;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px !important;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_2025 {
|
||||||
|
display: block;
|
||||||
|
background: url(../images/icon_2025.png) no-repeat;
|
||||||
|
background-size: 92px 82px;
|
||||||
|
}
|
||||||
|
.icon_2025.checkbox {
|
||||||
|
width: 18px;
|
||||||
|
height: 23px;
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
.icon_2025.boxreview {
|
||||||
|
width: 18px;
|
||||||
|
height: 23px;
|
||||||
|
background-position: -34px 0;
|
||||||
|
}
|
||||||
|
.icon_2025.cart {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
background-position: -68px 0;
|
||||||
|
}
|
||||||
|
.icon_2025.up {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: 1px -29px;
|
||||||
|
}
|
||||||
|
.icon_2025.thuhut {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: -34px -29px;
|
||||||
|
}
|
||||||
|
.icon_2025.tietkiem {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: -69px -29px;
|
||||||
|
}
|
||||||
|
.icon_2025.up-sale {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: 1px -58px;
|
||||||
|
}
|
||||||
|
.icon_2025.setting {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: -33px -58px;
|
||||||
|
}
|
||||||
|
.icon_2025.support {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-position: -68px -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homepage .banner {
|
||||||
|
background: url(../images/background-banner.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
min-height: 920px;
|
||||||
|
}
|
||||||
|
.homepage .background-tamnhin {
|
||||||
|
background: url(../images/background-tamnhin.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-image {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-image:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
z-index: 2;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 100%);
|
||||||
|
transform: skewX(-25deg);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-image:hover:before {
|
||||||
|
animation: image 1.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes image {
|
||||||
|
100% {
|
||||||
|
left: 125%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.breadcrumb li:last-child i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li a span:hover {
|
||||||
|
color: var(--color-blue);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-pricing {
|
||||||
|
background: url(../images/background-pricing.png) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-compare li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 15px;
|
||||||
|
color: #5d5d69;
|
||||||
|
}
|
||||||
|
.content-compare li::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: 0;
|
||||||
|
top: 7px;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
background: #000;
|
||||||
|
clip-path: polygon(100% 0, 0% 100%, 100% 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-free {
|
||||||
|
background: url(../images/background-banner.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
min-height: 640px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input label::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "*";
|
||||||
|
right: -10px;
|
||||||
|
top: -3px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select select:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.form-select option {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.form-select label::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "*";
|
||||||
|
right: -10px;
|
||||||
|
top: -3px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-login {
|
||||||
|
background: url(../images/background-login.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
}/*# sourceMappingURL=style.css.map */
|
||||||
1
assets/script/style.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["style.scss","style.css"],"names":[],"mappings":"AAAA;EACI,qBAAA;EACA,qBAAA;ACCJ;;ADCA;EACI,yBAAA;EACA,mBAAA;ACEJ;;ADAA;EACI,UAAA;EACA,sBAAA;ACGJ;;ADDA;EACI,mBAAA;EACA,mBAAA;EACA,gIAAA;ACIJ;;ADKA;EACI,iCAAA;EACA,eAAA;EACA,mBAAA;EACA,iBAAA;EACA,aAAA;EACA,sBAAA;EACA,iBAAA;EACA,8BAAA;ACFJ;;ADIA;EACI,iCAAA;ACDJ;;ADGA;EACI,WAAA;EACA,WAAA;EACA,cAAA;ACAJ;;ADEA;EACI,4BAAA;EACA,cAAA;ACCJ;;ADEA;EACI,cAAA;EACA,kDAAA;EACA,0BAAA;ACCJ;ADAI;EACI,WAAA;EACA,YAAA;EACA,wBAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,4BAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,4BAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,8BAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,gCAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,gCAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,8BAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,gCAAA;ACER;ADAI;EACI,WAAA;EACA,YAAA;EACA,gCAAA;ACER;;ADGI;EACI,0DAAA;EACA,0BAAA;EACA,iBAAA;ACAR;ADEI;EACI,2DAAA;EACA,0BAAA;EACA,2BAAA;ACAR;;ADIA;EACI,kBAAA;EACA,gBAAA;ACDJ;;ADIA;EACI,kBAAA;EACA,MAAA;EACA,WAAA;EACA,UAAA;EACA,cAAA;EACA,WAAA;EACA,UAAA;EACA,YAAA;EACA,+FAAA;EACA,wBAAA;EACA,eAAA;ACDJ;;ADIA;EACI,qBAAA;ACDJ;;ADIA;EACI;IACI,UAAA;ECDN;AACF;ADIA;EACI,aAAA;ACFJ;;ADKA;EACI,wBAAA;EACA,iBAAA;ACFJ;;ADKA;EACI,2DAAA;EACA,0BAAA;EACA,kCAAA;ACFJ;;ADMI;EACI,mBAAA;EACA,kBAAA;EACA,kBAAA;EACA,cAAA;ACHR;ADIQ;EACI,kBAAA;EACA,WAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EACA,gBAAA;EACA,8CAAA;ACFZ;;ADMA;EACI,0DAAA;EACA,0BAAA;EACA,iBAAA;ACHJ;;ADQQ;EACI,kBAAA;EACA,YAAA;EACA,YAAA;EACA,SAAA;EACA,UAAA;ACLZ;;ADWI;EACI,aAAA;ACRR;ADUI;EACI,eAAA;ACRR;ADWQ;EACI,kBAAA;EACA,YAAA;EACA,YAAA;EACA,SAAA;EACA,UAAA;ACTZ;;ADcA;EACI,yDAAA;EACA,0BAAA;EACA,2BAAA;ACXJ","file":"style.css"}
|
||||||
214
assets/script/style.scss
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
:root {
|
||||||
|
--color-blue: #0f5edd;
|
||||||
|
--text-green: #00c75d;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #00112b;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
0 0,
|
||||||
|
0 100%,
|
||||||
|
color-stop(0.7, #00112b),
|
||||||
|
color-stop(0.5, transparent),
|
||||||
|
to(transparent)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #fcfcfc;
|
||||||
|
max-width: 1920px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
.clearfix::after {
|
||||||
|
content: "";
|
||||||
|
clear: both;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1200px !important;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon_2025 {
|
||||||
|
display: block;
|
||||||
|
background: url(../images/icon_2025.png) no-repeat;
|
||||||
|
background-size: 92px 82px;
|
||||||
|
&.checkbox {
|
||||||
|
width: 18px;
|
||||||
|
height: 23px;
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
&.boxreview {
|
||||||
|
width: 18px;
|
||||||
|
height: 23px;
|
||||||
|
background-position: -34px 0;
|
||||||
|
}
|
||||||
|
&.cart {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
background-position: -68px 0;
|
||||||
|
}
|
||||||
|
&.up {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: 1px -29px;
|
||||||
|
}
|
||||||
|
&.thuhut {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: -34px -29px;
|
||||||
|
}
|
||||||
|
&.tietkiem {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: -69px -29px;
|
||||||
|
}
|
||||||
|
&.up-sale {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: 1px -58px;
|
||||||
|
}
|
||||||
|
&.setting {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background-position: -33px -58px;
|
||||||
|
}
|
||||||
|
&.support {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-position: -68px -60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.homepage {
|
||||||
|
.banner {
|
||||||
|
background: url(../images/background-banner.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
min-height: 920px;
|
||||||
|
}
|
||||||
|
.background-tamnhin {
|
||||||
|
background: url(../images/background-tamnhin.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-image {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-image:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
z-index: 2;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 100%);
|
||||||
|
transform: skewX(-25deg);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.effect-image:hover:before {
|
||||||
|
animation: image 1.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes image {
|
||||||
|
100% {
|
||||||
|
left: 125%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li:last-child i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li a span:hover {
|
||||||
|
color: var(--color-blue);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-pricing {
|
||||||
|
background: url(../images/background-pricing.png) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-compare {
|
||||||
|
li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 15px;
|
||||||
|
color: #5d5d69;
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: 0;
|
||||||
|
top: 7px;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
background: #000;
|
||||||
|
clip-path: polygon(100% 0, 0% 100%, 100% 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background-free {
|
||||||
|
background: url(../images/background-banner.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
min-height: 640px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
label {
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "*";
|
||||||
|
right: -10px;
|
||||||
|
top: -3px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select {
|
||||||
|
select:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
option {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "*";
|
||||||
|
right: -10px;
|
||||||
|
top: -3px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-login {
|
||||||
|
background: url(../images/background-login.jpg) no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
1
data/features/conversion.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/features/create.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/features/grow.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/features/market.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/features/operate.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/free-trial/home.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/home/home.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/login/home.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/menu.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
1
data/pricing/home.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
261
inc/Hura8/AdminTemplateFilter.php
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Hura8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before attempting to make new filter, see built-in filters: https://shopify.github.io/liquid/
|
||||||
|
*/
|
||||||
|
class AdminTemplateFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function to_json(array $array) {
|
||||||
|
return \json_encode($array);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create array of ['key' => '', 'value' => ] from [key1 => value1, key2=>value2, ...]
|
||||||
|
*
|
||||||
|
* @param array $key_values [key1 => value1, key2=>value2]
|
||||||
|
*
|
||||||
|
* @return array [['key' => 'key1', 'value' => value1], ['key' => 'key2', 'value' => value2]]
|
||||||
|
*/
|
||||||
|
public static function to_array(array $key_values) {
|
||||||
|
$result = [];
|
||||||
|
foreach ($key_values as $key => $value) {
|
||||||
|
$result[] = [
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $value,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* split a s by line to create array
|
||||||
|
*
|
||||||
|
* @param string $txt
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function get_line($txt) {
|
||||||
|
|
||||||
|
if(is_array($txt)) {
|
||||||
|
return $txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
$txt = trim($txt);
|
||||||
|
if( ! $txt ) return [];
|
||||||
|
|
||||||
|
return preg_split("/\n/", $txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement strlen
|
||||||
|
*
|
||||||
|
* @param string $str
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function length($str) {
|
||||||
|
return strlen(trim($str));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make number easier to read: 1000 -> 1.000
|
||||||
|
*
|
||||||
|
* @param string $number
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function format_number($number) {
|
||||||
|
if(!$number) return '';
|
||||||
|
$number = floatval($number);
|
||||||
|
|
||||||
|
$number = number_format($number, 0, ",", "."); //Vietnamese format with decimals by a coma
|
||||||
|
|
||||||
|
return $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function format_price($p_price, $currency = ''){
|
||||||
|
if(!$p_price) return '';
|
||||||
|
if(!$currency) $currency = (defined("DEFAULT_CURRENCY")) ? DEFAULT_CURRENCY : "vnd";
|
||||||
|
//if(is_string($p_price)) return 0;
|
||||||
|
if($currency == 'usd') {
|
||||||
|
return number_format($p_price,2,".",",");
|
||||||
|
}else {
|
||||||
|
return number_format($p_price,0,",",".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function global_asset_url($file_name = '')
|
||||||
|
{
|
||||||
|
return GLOBAL_ASSETS_PATH . $file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Description: get the shop's full asset url for template's images/js/css
|
||||||
|
*
|
||||||
|
* //Returns the URL of a file in the "assets" folder of a theme.
|
||||||
|
// {{ 'shop.css' | asset_url : 'arg1', 'arg2' ...}} -> //cdn.shopify.com/s/files/1/0087/0462/t/394/assets/shop.css?28253
|
||||||
|
*
|
||||||
|
* @param string $file_name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function asset_url($file_name = '')
|
||||||
|
{
|
||||||
|
if( !$file_name ) return '';
|
||||||
|
|
||||||
|
$file_ext = strtolower(strrchr($file_name, "."));
|
||||||
|
|
||||||
|
// script tags
|
||||||
|
if(in_array($file_ext, ['.js', '.css'])) return TEMPLATE_ASSET . "/script/" . $file_name;
|
||||||
|
|
||||||
|
// default image
|
||||||
|
return TEMPLATE_ASSET . "/images/" . $file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Description: construct a full html tag for images/js/css file
|
||||||
|
*
|
||||||
|
* @param string $file_path domain.com/static/style.css?v=3.1.1
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function script_tag($file_path) {
|
||||||
|
if( ! $file_path ) return '';
|
||||||
|
|
||||||
|
//check for ?
|
||||||
|
if(strpos($file_path, "?") !== false) {
|
||||||
|
$file_ext = str_replace(strrchr($file_path, "?"), "", $file_path);
|
||||||
|
$file_ext = strtolower(strrchr($file_ext, "."));
|
||||||
|
} else {
|
||||||
|
$file_ext = strtolower(strrchr($file_path, "."));
|
||||||
|
}
|
||||||
|
|
||||||
|
$tag_config = [
|
||||||
|
".css" => "<link rel=\"stylesheet\" href=\"".$file_path."\" type=\"text/css\" />",
|
||||||
|
".js" => "<script src=\"".$file_path."\"></script>",
|
||||||
|
".jpg" => "<img src=\"".$file_path."\" alt=\"n\"/>",
|
||||||
|
".jpeg" => "<img src=\"".$file_path."\" alt=\"\"/>",
|
||||||
|
".gif" => "<img src=\"".$file_path."\" alt=\"\"/>",
|
||||||
|
".png" => "<img src=\"".$file_path."\" alt=\"\"/>",
|
||||||
|
];
|
||||||
|
|
||||||
|
return (isset($tag_config[$file_ext])) ? $tag_config[$file_ext] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {{ product_info.main_image | img_url: '300x300' }} => https://cdn.shopify.com/s/files/1/1183/1048/products/boat-shoes_300x300.jpeg?1459175177
|
||||||
|
* @param string $full_path
|
||||||
|
* @param string $modifier
|
||||||
|
* $modifier:
|
||||||
|
* - must be in format: NumberxNumber or Numberx where Number must within 10 -> 9999
|
||||||
|
* - or be one of these: small | medium | large
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function img_url($full_path, $modifier)
|
||||||
|
{
|
||||||
|
$clean_modifier = ($modifier) ? trim($modifier) : "";
|
||||||
|
|
||||||
|
// verify $modifier
|
||||||
|
// must be in format: NumberxNumber or Numberx where Number must within 10 -> 9999
|
||||||
|
if($clean_modifier
|
||||||
|
&& !preg_match("/^[0-9]{2,4}x([0-9]{2,4})?$/i", $clean_modifier)
|
||||||
|
&& !in_array($clean_modifier, ["small", "medium", "large"])
|
||||||
|
) {
|
||||||
|
$clean_modifier = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// return if no valid modifier
|
||||||
|
if( ! $clean_modifier ) {
|
||||||
|
return $full_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_dot_position = strrpos($full_path, ".");
|
||||||
|
if( ! $last_dot_position ) return $full_path . $clean_modifier;
|
||||||
|
|
||||||
|
return join("", [
|
||||||
|
substr($full_path, 0, $last_dot_position),
|
||||||
|
"_",
|
||||||
|
$clean_modifier,
|
||||||
|
substr($full_path, $last_dot_position)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* //Returns the URL of a file in the Files page of the admin.
|
||||||
|
//{{ 'size-chart.pdf' | file_url }} -> //cdn.shopify.com/s/files/1/0087/0462/files/size-chart.pdf?28261
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param string $string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function file_url($input, $string)
|
||||||
|
{
|
||||||
|
return strtoupper($input) . " = " . $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* //Returns the asset URL of an image in the Files page of the admin. file_img_url accepts an image size parameter.
|
||||||
|
//{{ 'logo.png' | file_img_url: '1024x768' }} -> //cdn.shopify.com/s/files/1/0246/0527/files/logo_1024x768.png?42
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param string $string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function file_img_url($input, $string)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show all content of a variable, useful for template development
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function print_r($input)
|
||||||
|
{
|
||||||
|
@ob_start();
|
||||||
|
print_r($input);
|
||||||
|
$content = ob_get_contents();
|
||||||
|
@ob_end_clean();
|
||||||
|
|
||||||
|
return join("\r", ['<!-- print_r debug content: ', $content, '-->']) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show all content of a variable, useful for template development
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function show_var($input)
|
||||||
|
{
|
||||||
|
@ob_start();
|
||||||
|
print_r($input);
|
||||||
|
$content = ob_get_contents();
|
||||||
|
@ob_end_clean();
|
||||||
|
|
||||||
|
return join("\r", ['<textarea cols="80" rows="20">', $content, '</textarea>']) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
212
inc/Hura8/AppAdmin.php
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Hura8;
|
||||||
|
|
||||||
|
use Liquid\Liquid;
|
||||||
|
use Liquid\Template as LiquidTemplate;
|
||||||
|
|
||||||
|
class AppAdmin
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $tpl_path = "template";
|
||||||
|
|
||||||
|
protected $current_route_info = [
|
||||||
|
"module" => 'home',
|
||||||
|
"view" => 'home',
|
||||||
|
"url" => '/admin/product'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $data = [];
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the app
|
||||||
|
public function start() {
|
||||||
|
$this->getRouter();
|
||||||
|
$this->getData();
|
||||||
|
echo $this->renderModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function getRouter() {
|
||||||
|
/*$route = [
|
||||||
|
"module" => (isset($_REQUEST['module'])) ? $_REQUEST['module'] : 'home',
|
||||||
|
"view" => (isset($_REQUEST['view'])) ? $_REQUEST['view'] : 'home',
|
||||||
|
];*/
|
||||||
|
|
||||||
|
$objRouter = new Router();
|
||||||
|
$this->current_route_info = $objRouter->getRouting();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function getData() {
|
||||||
|
$module_file = $this->getModuleFile();
|
||||||
|
|
||||||
|
if(file_exists($module_file)) {
|
||||||
|
// print_r($this->current_route_info);
|
||||||
|
// die('Page '. $module_file .' not found!');
|
||||||
|
$data = include_once $module_file;
|
||||||
|
}else{
|
||||||
|
$data = ['file data '. $module_file .' not found!'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$global_data = [
|
||||||
|
"module" => $this->current_route_info['module'],
|
||||||
|
"view" => $this->current_route_info['view'],
|
||||||
|
"url" => $this->current_route_info['url'],
|
||||||
|
"main_menu" => include_once ROOT_DIR."/data/menu.php",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->data = array(
|
||||||
|
'global' => $global_data,
|
||||||
|
// module-specific data, just print {{ page }} to see all available data for the current page!!!
|
||||||
|
'page' => (is_array($data)) ? $data : [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function getModuleFile() {
|
||||||
|
return join(DIRECTORY_SEPARATOR, [
|
||||||
|
"data",
|
||||||
|
$this->current_route_info["module"],
|
||||||
|
str_replace("-", "_", $this->current_route_info["view"]).".php"
|
||||||
|
]) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function renderModule() {
|
||||||
|
|
||||||
|
if(!$this->current_route_info['module'] || !$this->current_route_info['view']) {
|
||||||
|
die("Module not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
$template_file_path = $this->tpl_path ."/". $this->current_route_info['module'];
|
||||||
|
$template_file_name = str_replace("-", '_', $this->current_route_info['view']).".html";
|
||||||
|
$template_file_full_path = $template_file_path."/".$template_file_name;
|
||||||
|
|
||||||
|
//check exist
|
||||||
|
if(!@file_exists( $template_file_full_path)) {
|
||||||
|
// attempt to auto create first
|
||||||
|
// todo: this MUST BE TURNED OFF IN PRODUCTION, else many files will be created unintentionally
|
||||||
|
$module_file = $this->getModuleFile();
|
||||||
|
// only create if module file exist
|
||||||
|
if(file_exists($module_file) && !$this->autoCreateTplFile( $template_file_path, $template_file_name )) {
|
||||||
|
die("Please manually create template file at: ". $template_file_full_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$theme_file_path = $this->tpl_path ."/theme.html";
|
||||||
|
if( ! @file_exists( $theme_file_path)) {
|
||||||
|
die("Theme not exist (please create): " . $theme_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$theme_content = @file_get_contents( $theme_file_path );
|
||||||
|
$module_content = @file_get_contents( $template_file_full_path );
|
||||||
|
|
||||||
|
$page_content_to_parse = preg_replace([
|
||||||
|
"/{{(\s+)?page_content(\s+)?}}/"
|
||||||
|
], [
|
||||||
|
$module_content,
|
||||||
|
] , $theme_content );
|
||||||
|
|
||||||
|
|
||||||
|
return $this->parse(
|
||||||
|
$page_content_to_parse,
|
||||||
|
$template_file_path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected function autoCreateTplFile($file_path, $file_name) : bool {
|
||||||
|
// create dir if not exist
|
||||||
|
if(!file_exists($file_path)) {
|
||||||
|
if(!mkdir($file_path, 0755, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!file_exists($file_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//create file
|
||||||
|
$file_full_path = $file_path . "/". $file_name;
|
||||||
|
@file_put_contents($file_full_path, $file_full_path);
|
||||||
|
|
||||||
|
return file_exists($file_full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2 ways to render a html template
|
||||||
|
* 1. Use $html_to_parse, which requires no dependencies
|
||||||
|
* Example:
|
||||||
|
* Template::parse(null, 'Age = {{age}}', ['age' => 21], '');
|
||||||
|
*
|
||||||
|
* 2. Use $template_file_path, which requires dependency $path
|
||||||
|
* Template::parse(Template::$setting_template_path, null, ['age' => 21], 'email/test');
|
||||||
|
* */
|
||||||
|
protected function parse($html_to_parse = null, $template_file_path = '') {
|
||||||
|
|
||||||
|
if(!$html_to_parse && !$template_file_path) {
|
||||||
|
return 'Nothing to parse';
|
||||||
|
}
|
||||||
|
|
||||||
|
//output to html
|
||||||
|
Liquid::set('INCLUDE_SUFFIX', 'html');
|
||||||
|
Liquid::set('INCLUDE_PREFIX', '');
|
||||||
|
//Liquid::set('INCLUDE_ALLOW_EXT', true);
|
||||||
|
Liquid::set('ESCAPE_BY_DEFAULT', false);
|
||||||
|
|
||||||
|
$enable_cache = false; // default = true, turn this on-off to disable cache while working on local mode
|
||||||
|
//$enable_cache = true;
|
||||||
|
|
||||||
|
//catch exception and print friendly notice
|
||||||
|
try {
|
||||||
|
|
||||||
|
$objLiquidTemplate = new LiquidTemplate( $this->tpl_path );
|
||||||
|
$objLiquidTemplate->registerFilter( AdminTemplateFilter::class );
|
||||||
|
if($enable_cache) {
|
||||||
|
/*$objLiquidTemplate->setCache(new File([
|
||||||
|
'cache_dir' => self::$cache_dir
|
||||||
|
]));*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if($html_to_parse) {
|
||||||
|
$objLiquidTemplate->parse($html_to_parse);
|
||||||
|
}elseif ($template_file_path) {
|
||||||
|
$objLiquidTemplate->parseFile($template_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $objLiquidTemplate->render($this->data);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$result = [];
|
||||||
|
do {
|
||||||
|
//printf("%s:%d %s (%d) [%s]\n", $e->getFile(), $e->getLine(), $e->getMessage(), $e->getCode(), get_class($e));
|
||||||
|
//echo $e->getTraceAsString();
|
||||||
|
//$code = $e->getTrace()[0]['args'][0];
|
||||||
|
//if(is_array($code)) $code = serialize($code);
|
||||||
|
$result[] = sprintf(
|
||||||
|
"
|
||||||
|
Lỗi code trong file template html: <br />
|
||||||
|
- Chi tiết lỗi: %s<br />
|
||||||
|
- File template: %s<br />
|
||||||
|
- Hướng dẫn xử lý: Tách từng phần html để kiểm tra và nhấn F5 mỗi lần. Nếu không xuất hiện thông báo này nghĩa là phần đó không tạo lỗi
|
||||||
|
",
|
||||||
|
$e->getMessage(),
|
||||||
|
substr($template_file_path, strrpos($template_file_path, DIRECTORY_SEPARATOR) + 1 ),
|
||||||
|
//static::$cache_dir
|
||||||
|
);
|
||||||
|
|
||||||
|
} while($e = $e->getPrevious());
|
||||||
|
|
||||||
|
return join(" - ", $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
inc/Hura8/Components/Analytics/Model/TrackDeviceInfo.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
inc/Hura8/Components/Analytics/Model/TrackRouteInfo.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
inc/Hura8/Components/Analytics/Model/TrackUserInfo.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
inc/Hura8/Components/Analytics/Model/TrackingModel.php
Normal 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.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
33
inc/Hura8/Components/Article/Model/ArticleCategoryModel.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
19
inc/Hura8/Components/Article/Model/ArticleLanguageModel.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
150
inc/Hura8/Components/Article/Model/ArticleModel.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
29
inc/Hura8/Components/Article/Model/ArticleSearchModel.php
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
55
inc/Hura8/Components/Article/Model/UArticleModel.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
67
inc/Hura8/Components/Banner/Controller/bBannerController.php
Normal 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 & 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
30
inc/Hura8/Components/Banner/Model/BannerLocationModel.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
90
inc/Hura8/Components/Banner/Model/BannerModel.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
33
inc/Hura8/Components/Banner/Model/BannerSearchModel.php
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
73
inc/Hura8/Components/Brand/Controller/bBrandController.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
19
inc/Hura8/Components/Brand/Model/BrandLanguageModel.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
inc/Hura8/Components/Brand/Model/BrandModel.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
148
inc/Hura8/Components/ComboSet/Controller/bComboSetController.php
Normal 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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
165
inc/Hura8/Components/ComboSet/Model/ComboSetModel.php
Normal 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
500
inc/Hura8/Components/ConfigGroup/Model/ConfigGroupModel.php
Normal 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]) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Hura8\Components\Customer\AdminController;
|
||||||
|
|
||||||
|
use Hura8\Components\Customer\Controller\bCustomerLoyaltyController;
|
||||||
|
|
||||||
|
class ACustomerLoyaltyController extends bCustomerLoyaltyController {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
79
inc/Hura8/Components/Customer/Model/CustomerAuthModel.php
Normal 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,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
213
inc/Hura8/Components/Customer/Model/CustomerGroupModel.php
Normal 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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
268
inc/Hura8/Components/Customer/Model/CustomerLoyaltyModel.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
128
inc/Hura8/Components/Customer/Model/CustomerModel.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||