refactor(Core): refactor and optimize code
This commit is contained in:
94
app/Controllers/BaseController.php
Normal file
94
app/Controllers/BaseController.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
namespace Sodino\Controllers;
|
||||
|
||||
use Sodino\Core\Validator;
|
||||
|
||||
/**
|
||||
* Base Controller
|
||||
*/
|
||||
abstract class BaseController {
|
||||
|
||||
/**
|
||||
* Verify nonce
|
||||
*/
|
||||
protected function verifyNonce($nonce_field, $nonce_action) {
|
||||
if (!isset($_POST[$nonce_field]) || !wp_verify_nonce($_POST[$nonce_field], $nonce_action)) {
|
||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect with message
|
||||
*/
|
||||
protected function redirect($url, $message = '', $type = 'success') {
|
||||
if ($message) {
|
||||
set_transient('sodino_admin_notice', [
|
||||
'message' => $message,
|
||||
'type' => $type
|
||||
], 30);
|
||||
}
|
||||
wp_safe_redirect($url);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sanitized POST data
|
||||
*/
|
||||
protected function getPostData($key, $default = '') {
|
||||
return isset($_POST[$key]) ? sanitize_text_field($_POST[$key]) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sanitized GET data
|
||||
*/
|
||||
protected function getQueryData($key, $default = '') {
|
||||
return isset($_GET[$key]) ? sanitize_text_field($_GET[$key]) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate data
|
||||
*/
|
||||
protected function validate(array $data) {
|
||||
return Validator::make($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render view
|
||||
*/
|
||||
protected function render($view, $data = []) {
|
||||
extract($data);
|
||||
$view_file = SODINO_PLUGIN_DIR . 'admin/views/' . $view . '.php';
|
||||
|
||||
if (file_exists($view_file)) {
|
||||
include $view_file;
|
||||
} else {
|
||||
wp_die(sprintf(__('View file not found: %s', 'sodino'), $view));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user capability
|
||||
*/
|
||||
protected function checkCapability($capability = 'manage_options') {
|
||||
if (!current_user_can($capability)) {
|
||||
wp_die(__('شما دسترسی لازم برای این عملیات را ندارید.', 'sodino'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show admin notice
|
||||
*/
|
||||
public function showAdminNotice() {
|
||||
$notice = get_transient('sodino_admin_notice');
|
||||
|
||||
if ($notice) {
|
||||
$class = $notice['type'] === 'error' ? 'notice-error' : 'notice-success';
|
||||
printf(
|
||||
'<div class="notice %s is-dismissible"><p>%s</p></div>',
|
||||
esc_attr($class),
|
||||
esc_html($notice['message'])
|
||||
);
|
||||
delete_transient('sodino_admin_notice');
|
||||
}
|
||||
}
|
||||
}
|
||||
48
app/Controllers/DashboardController.php
Normal file
48
app/Controllers/DashboardController.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace Sodino\Controllers;
|
||||
|
||||
use Sodino\Repositories\EventRepository;
|
||||
use Sodino\Repositories\RuleRepository;
|
||||
use Sodino\Services\AnalyticsService;
|
||||
|
||||
/**
|
||||
* Dashboard Controller
|
||||
*/
|
||||
class DashboardController extends BaseController {
|
||||
private $analyticsService;
|
||||
|
||||
public function __construct(EventRepository $eventRepository, RuleRepository $ruleRepository) {
|
||||
$this->analyticsService = new AnalyticsService($eventRepository, $ruleRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard page
|
||||
*/
|
||||
public function index() {
|
||||
$this->checkCapability();
|
||||
|
||||
$filters = [
|
||||
'range' => $this->getQueryData('range', '7d'),
|
||||
'start_date' => $this->getQueryData('start_date', ''),
|
||||
'end_date' => $this->getQueryData('end_date', ''),
|
||||
'product_id' => intval($this->getQueryData('product_id', 0)),
|
||||
'category_id' => intval($this->getQueryData('category_id', 0)),
|
||||
];
|
||||
|
||||
if (!empty($filters['product_id'])) {
|
||||
$filters['product_ids'] = [$filters['product_id']];
|
||||
}
|
||||
|
||||
$dashboardData = $this->analyticsService->getDashboardData($filters);
|
||||
$productOptions = $this->analyticsService->getProductOptions();
|
||||
$categoryOptions = $this->analyticsService->getCategoryOptions();
|
||||
|
||||
$this->render('dashboard', [
|
||||
'dashboardData' => $dashboardData,
|
||||
'productOptions' => $productOptions,
|
||||
'categoryOptions' => $categoryOptions,
|
||||
'filters' => $filters,
|
||||
'current_page' => 'sodino-dashboard'
|
||||
]);
|
||||
}
|
||||
}
|
||||
189
app/Controllers/RuleController.php
Normal file
189
app/Controllers/RuleController.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
namespace Sodino\Controllers;
|
||||
|
||||
use Sodino\Repositories\RuleRepository;
|
||||
use Sodino\Models\Rule;
|
||||
|
||||
/**
|
||||
* Rule Controller
|
||||
*/
|
||||
class RuleController extends BaseController {
|
||||
private $ruleRepository;
|
||||
|
||||
public function __construct(RuleRepository $ruleRepository) {
|
||||
$this->ruleRepository = $ruleRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* List rules page
|
||||
*/
|
||||
public function index() {
|
||||
$this->checkCapability();
|
||||
|
||||
require_once SODINO_PLUGIN_DIR . 'admin/class-rules-list-table.php';
|
||||
$rulesTable = new \Sodino_Rules_List_Table($this->ruleRepository);
|
||||
$rulesTable->prepare_items();
|
||||
|
||||
$this->render('rules-list', [
|
||||
'rulesTable' => $rulesTable,
|
||||
'current_page' => 'sodino-rules'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create rule page
|
||||
*/
|
||||
public function create() {
|
||||
$this->checkCapability();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
return $this->store();
|
||||
}
|
||||
|
||||
$rule = new Rule();
|
||||
$this->render('rule-form', [
|
||||
'rule' => $rule,
|
||||
'current_page' => 'sodino-add-rule'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit rule page
|
||||
*/
|
||||
public function edit() {
|
||||
$this->checkCapability();
|
||||
|
||||
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||
$rule = $this->ruleRepository->getById($id);
|
||||
|
||||
if (!$rule) {
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-rules'),
|
||||
__('قانون یافت نشد.', 'sodino'),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
return $this->update($rule);
|
||||
}
|
||||
|
||||
$this->render('rule-form', [
|
||||
'rule' => $rule,
|
||||
'current_page' => 'sodino-add-rule'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store new rule
|
||||
*/
|
||||
private function store() {
|
||||
$this->verifyNonce('sodino_rule_nonce', 'sodino_save_rule');
|
||||
|
||||
$validator = $this->validate($_POST);
|
||||
$validator->required('name', __('نام قانون الزامی است.', 'sodino'))
|
||||
->numeric('priority')
|
||||
->min('priority', 1)
|
||||
->numeric('usage_limit')
|
||||
->min('usage_limit', 0);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-add-rule'),
|
||||
$validator->firstError(),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
$rule = new Rule();
|
||||
$this->fillRuleFromPost($rule);
|
||||
$this->ruleRepository->save($rule);
|
||||
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-rules'),
|
||||
__('قانون با موفقیت ایجاد شد.', 'sodino')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing rule
|
||||
*/
|
||||
private function update($rule) {
|
||||
$this->verifyNonce('sodino_rule_nonce', 'sodino_save_rule');
|
||||
|
||||
$validator = $this->validate($_POST);
|
||||
$validator->required('name', __('نام قانون الزامی است.', 'sodino'))
|
||||
->numeric('priority')
|
||||
->min('priority', 1)
|
||||
->numeric('usage_limit')
|
||||
->min('usage_limit', 0);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-add-rule&action=edit&id=' . $rule->id),
|
||||
$validator->firstError(),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
$this->fillRuleFromPost($rule);
|
||||
$this->ruleRepository->save($rule);
|
||||
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-rules'),
|
||||
__('قانون با موفقیت بهروزرسانی شد.', 'sodino')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete rule
|
||||
*/
|
||||
public function delete() {
|
||||
$this->checkCapability();
|
||||
|
||||
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'delete_rule')) {
|
||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||
}
|
||||
|
||||
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||
$this->ruleRepository->delete($id);
|
||||
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-rules'),
|
||||
__('قانون با موفقیت حذف شد.', 'sodino')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill rule from POST data
|
||||
*/
|
||||
private function fillRuleFromPost($rule) {
|
||||
$rule->name = sanitize_text_field($_POST['name'] ?? '');
|
||||
$rule->priority = max(1, intval($_POST['priority'] ?? 10));
|
||||
$rule->usage_limit = max(0, intval($_POST['usage_limit'] ?? 0));
|
||||
$rule->user_roles = array_map('sanitize_text_field', (array) ($_POST['user_roles'] ?? []));
|
||||
$rule->start_date = !empty($_POST['start_date']) ? sanitize_text_field($_POST['start_date']) : null;
|
||||
$rule->end_date = !empty($_POST['end_date']) ? sanitize_text_field($_POST['end_date']) : null;
|
||||
$rule->enabled = isset($_POST['enabled']) ? 1 : 0;
|
||||
|
||||
// Parse conditions
|
||||
if (isset($_POST['conditions']) && is_array($_POST['conditions'])) {
|
||||
$rule->conditions = array_map(function($condition) {
|
||||
return [
|
||||
'type' => sanitize_text_field($condition['type'] ?? ''),
|
||||
'value' => sanitize_text_field($condition['value'] ?? '')
|
||||
];
|
||||
}, $_POST['conditions']);
|
||||
}
|
||||
|
||||
// Parse actions
|
||||
if (isset($_POST['actions']) && is_array($_POST['actions'])) {
|
||||
$rule->actions = array_map(function($action) {
|
||||
return [
|
||||
'type' => sanitize_text_field($action['type'] ?? ''),
|
||||
'value' => sanitize_text_field($action['value'] ?? '')
|
||||
];
|
||||
}, $_POST['actions']);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
app/Controllers/SettingsController.php
Normal file
87
app/Controllers/SettingsController.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace Sodino\Controllers;
|
||||
|
||||
use Sodino\Core\Settings;
|
||||
use Sodino\Core\Cache;
|
||||
|
||||
/**
|
||||
* Settings Controller
|
||||
*/
|
||||
class SettingsController extends BaseController {
|
||||
private $settings;
|
||||
private $cache;
|
||||
|
||||
public function __construct() {
|
||||
$this->settings = Settings::getInstance();
|
||||
$this->cache = Cache::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings page
|
||||
*/
|
||||
public function index() {
|
||||
$this->checkCapability();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
return $this->save();
|
||||
}
|
||||
|
||||
$settings = $this->settings->all();
|
||||
$this->render('settings', [
|
||||
'settings' => $settings,
|
||||
'current_page' => 'sodino-settings'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings
|
||||
*/
|
||||
private function save() {
|
||||
$this->verifyNonce('sodino_settings_nonce', 'sodino_save_settings');
|
||||
|
||||
$settings = [
|
||||
'plugin_enabled' => isset($_POST['plugin_enabled']) ? 1 : 0,
|
||||
'pricing_enabled' => isset($_POST['pricing_enabled']) ? 1 : 0,
|
||||
'upsell_enabled' => isset($_POST['upsell_enabled']) ? 1 : 0,
|
||||
'banner_enabled' => isset($_POST['banner_enabled']) ? 1 : 0,
|
||||
'allow_multiple_rules' => isset($_POST['allow_multiple_rules']) ? 1 : 0,
|
||||
'strategy' => sanitize_text_field($_POST['strategy'] ?? 'priority'),
|
||||
'max_discount_percent' => max(0, min(100, floatval($_POST['max_discount_percent'] ?? 100))),
|
||||
'min_product_price' => max(0, floatval($_POST['min_product_price'] ?? 0)),
|
||||
'ab_testing_enabled' => isset($_POST['ab_testing_enabled']) ? 1 : 0,
|
||||
'cart_pricing_enabled' => isset($_POST['cart_pricing_enabled']) ? 1 : 0,
|
||||
'scheduled_campaigns_enabled' => isset($_POST['scheduled_campaigns_enabled']) ? 1 : 0,
|
||||
'cache_enabled' => isset($_POST['cache_enabled']) ? 1 : 0,
|
||||
'cache_duration' => max(60, intval($_POST['cache_duration'] ?? 3600)),
|
||||
'debug_mode' => isset($_POST['debug_mode']) ? 1 : 0,
|
||||
];
|
||||
|
||||
$this->settings->update($settings);
|
||||
|
||||
// Clear cache when settings change
|
||||
$this->cache->clearAll();
|
||||
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-settings'),
|
||||
__('تنظیمات با موفقیت ذخیره شد.', 'sodino')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache action
|
||||
*/
|
||||
public function clearCache() {
|
||||
$this->checkCapability();
|
||||
|
||||
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'clear_cache')) {
|
||||
wp_die(__('خطای امنیتی رخ داد.', 'sodino'));
|
||||
}
|
||||
|
||||
$this->cache->clearAll();
|
||||
|
||||
$this->redirect(
|
||||
admin_url('admin.php?page=sodino-settings'),
|
||||
__('کش با موفقیت پاک شد.', 'sodino')
|
||||
);
|
||||
}
|
||||
}
|
||||
137
app/Core/Cache.php
Normal file
137
app/Core/Cache.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
namespace Sodino\Core;
|
||||
|
||||
/**
|
||||
* Cache Manager
|
||||
* Handles caching with WordPress transients and custom database cache
|
||||
*/
|
||||
class Cache {
|
||||
private static $instance = null;
|
||||
private $memory_cache = [];
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached value
|
||||
*/
|
||||
public function get($key, $group = 'sodino') {
|
||||
$full_key = $this->buildKey($key, $group);
|
||||
|
||||
// Check memory cache first
|
||||
if (isset($this->memory_cache[$full_key])) {
|
||||
return $this->memory_cache[$full_key];
|
||||
}
|
||||
|
||||
// Check WordPress transient
|
||||
$value = get_transient($full_key);
|
||||
|
||||
if ($value !== false) {
|
||||
$this->memory_cache[$full_key] = $value;
|
||||
return $value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cached value
|
||||
*/
|
||||
public function set($key, $value, $expiration = 3600, $group = 'sodino') {
|
||||
$full_key = $this->buildKey($key, $group);
|
||||
|
||||
// Set in memory cache
|
||||
$this->memory_cache[$full_key] = $value;
|
||||
|
||||
// Set in WordPress transient
|
||||
return set_transient($full_key, $value, $expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cached value
|
||||
*/
|
||||
public function delete($key, $group = 'sodino') {
|
||||
$full_key = $this->buildKey($key, $group);
|
||||
|
||||
// Remove from memory cache
|
||||
unset($this->memory_cache[$full_key]);
|
||||
|
||||
// Remove from WordPress transient
|
||||
return delete_transient($full_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all cache for a group
|
||||
*/
|
||||
public function clearGroup($group = 'sodino') {
|
||||
global $wpdb;
|
||||
|
||||
// Clear memory cache for group
|
||||
foreach ($this->memory_cache as $key => $value) {
|
||||
if (strpos($key, "sodino_{$group}_") === 0) {
|
||||
unset($this->memory_cache[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear transients for group
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||||
$wpdb->esc_like('_transient_sodino_' . $group . '_') . '%'
|
||||
)
|
||||
);
|
||||
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||||
$wpdb->esc_like('_transient_timeout_sodino_' . $group . '_') . '%'
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all Sodino cache
|
||||
*/
|
||||
public function clearAll() {
|
||||
global $wpdb;
|
||||
|
||||
// Clear memory cache
|
||||
$this->memory_cache = [];
|
||||
|
||||
// Clear all Sodino transients
|
||||
$wpdb->query(
|
||||
"DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_sodino_%' OR option_name LIKE '_transient_timeout_sodino_%'"
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember pattern - get from cache or execute callback
|
||||
*/
|
||||
public function remember($key, $callback, $expiration = 3600, $group = 'sodino') {
|
||||
$value = $this->get($key, $group);
|
||||
|
||||
if ($value !== false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = call_user_func($callback);
|
||||
$this->set($key, $value, $expiration, $group);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build cache key
|
||||
*/
|
||||
private function buildKey($key, $group) {
|
||||
return "sodino_{$group}_{$key}";
|
||||
}
|
||||
}
|
||||
125
app/Core/Settings.php
Normal file
125
app/Core/Settings.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
namespace Sodino\Core;
|
||||
|
||||
/**
|
||||
* Settings Manager
|
||||
*/
|
||||
class Settings {
|
||||
private static $instance = null;
|
||||
private $settings = null;
|
||||
private $option_name = 'sodino_settings';
|
||||
|
||||
private $defaults = [
|
||||
'plugin_enabled' => 1,
|
||||
'pricing_enabled' => 1,
|
||||
'upsell_enabled' => 1,
|
||||
'banner_enabled' => 1,
|
||||
'allow_multiple_rules' => 0,
|
||||
'strategy' => 'priority',
|
||||
'max_discount_percent' => 100,
|
||||
'min_product_price' => 0,
|
||||
'ab_testing_enabled' => 0,
|
||||
'cart_pricing_enabled' => 1,
|
||||
'scheduled_campaigns_enabled' => 1,
|
||||
'cache_enabled' => 1,
|
||||
'cache_duration' => 3600,
|
||||
'debug_mode' => 0,
|
||||
];
|
||||
|
||||
public static function getInstance() {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all settings
|
||||
*/
|
||||
public function all() {
|
||||
if ($this->settings === null) {
|
||||
$this->settings = wp_parse_args(
|
||||
get_option($this->option_name, []),
|
||||
$this->defaults
|
||||
);
|
||||
}
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single setting
|
||||
*/
|
||||
public function get($key, $default = null) {
|
||||
$settings = $this->all();
|
||||
return $settings[$key] ?? $default ?? $this->defaults[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set single setting
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
$settings = $this->all();
|
||||
$settings[$key] = $value;
|
||||
$this->settings = $settings;
|
||||
return update_option($this->option_name, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple settings
|
||||
*/
|
||||
public function update(array $settings) {
|
||||
$current = $this->all();
|
||||
$this->settings = array_merge($current, $settings);
|
||||
return update_option($this->option_name, $this->settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to defaults
|
||||
*/
|
||||
public function reset() {
|
||||
$this->settings = $this->defaults;
|
||||
return update_option($this->option_name, $this->defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if plugin is enabled
|
||||
*/
|
||||
public function isEnabled() {
|
||||
return (bool) $this->get('plugin_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if pricing is enabled
|
||||
*/
|
||||
public function isPricingEnabled() {
|
||||
return $this->isEnabled() && (bool) $this->get('pricing_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if upsell is enabled
|
||||
*/
|
||||
public function isUpsellEnabled() {
|
||||
return $this->isEnabled() && (bool) $this->get('upsell_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if banner is enabled
|
||||
*/
|
||||
public function isBannerEnabled() {
|
||||
return $this->isEnabled() && (bool) $this->get('banner_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if cache is enabled
|
||||
*/
|
||||
public function isCacheEnabled() {
|
||||
return (bool) $this->get('cache_enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if debug mode is enabled
|
||||
*/
|
||||
public function isDebugMode() {
|
||||
return (bool) $this->get('debug_mode');
|
||||
}
|
||||
}
|
||||
132
app/Core/Validator.php
Normal file
132
app/Core/Validator.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
namespace Sodino\Core;
|
||||
|
||||
/**
|
||||
* Validation Helper
|
||||
*/
|
||||
class Validator {
|
||||
private $errors = [];
|
||||
private $data = [];
|
||||
|
||||
public function __construct(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate required field
|
||||
*/
|
||||
public function required($field, $message = null) {
|
||||
if (!isset($this->data[$field]) || empty($this->data[$field])) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s الزامی است.', 'sodino'), $field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate numeric field
|
||||
*/
|
||||
public function numeric($field, $message = null) {
|
||||
if (isset($this->data[$field]) && !is_numeric($this->data[$field])) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s باید عدد باشد.', 'sodino'), $field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate min value
|
||||
*/
|
||||
public function min($field, $min, $message = null) {
|
||||
if (isset($this->data[$field]) && $this->data[$field] < $min) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s باید حداقل %s باشد.', 'sodino'), $field, $min);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate max value
|
||||
*/
|
||||
public function max($field, $max, $message = null) {
|
||||
if (isset($this->data[$field]) && $this->data[$field] > $max) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s باید حداکثر %s باشد.', 'sodino'), $field, $max);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email
|
||||
*/
|
||||
public function email($field, $message = null) {
|
||||
if (isset($this->data[$field]) && !filter_var($this->data[$field], FILTER_VALIDATE_EMAIL)) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s باید یک ایمیل معتبر باشد.', 'sodino'), $field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate URL
|
||||
*/
|
||||
public function url($field, $message = null) {
|
||||
if (isset($this->data[$field]) && !filter_var($this->data[$field], FILTER_VALIDATE_URL)) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s باید یک URL معتبر باشد.', 'sodino'), $field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate in array
|
||||
*/
|
||||
public function in($field, array $values, $message = null) {
|
||||
if (isset($this->data[$field]) && !in_array($this->data[$field], $values, true)) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('مقدار فیلد %s نامعتبر است.', 'sodino'), $field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation
|
||||
*/
|
||||
public function custom($field, callable $callback, $message = null) {
|
||||
if (isset($this->data[$field]) && !call_user_func($callback, $this->data[$field])) {
|
||||
$this->errors[$field][] = $message ?? sprintf(__('فیلد %s نامعتبر است.', 'sodino'), $field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if validation passed
|
||||
*/
|
||||
public function passes() {
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if validation failed
|
||||
*/
|
||||
public function fails() {
|
||||
return !$this->passes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all errors
|
||||
*/
|
||||
public function errors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first error
|
||||
*/
|
||||
public function firstError() {
|
||||
foreach ($this->errors as $field => $messages) {
|
||||
return $messages[0] ?? '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method
|
||||
*/
|
||||
public static function make(array $data) {
|
||||
return new self($data);
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,11 @@ class Rule {
|
||||
public $actions;
|
||||
public $priority;
|
||||
public $usage_limit;
|
||||
public $usage_count;
|
||||
public $user_roles;
|
||||
public $start_date;
|
||||
public $end_date;
|
||||
public $enabled;
|
||||
public $condition_type;
|
||||
public $condition_value;
|
||||
public $action_type;
|
||||
public $action_value;
|
||||
public $created_at;
|
||||
public $updated_at;
|
||||
|
||||
@@ -32,28 +29,13 @@ class Rule {
|
||||
$this->actions = $this->parseJsonField($data['actions'] ?? '[]');
|
||||
$this->priority = isset($data['priority']) ? (int) $data['priority'] : 10;
|
||||
$this->usage_limit = isset($data['usage_limit']) ? (int) $data['usage_limit'] : 0;
|
||||
$this->usage_count = isset($data['usage_count']) ? (int) $data['usage_count'] : 0;
|
||||
$this->user_roles = $this->parseRolesField($data['user_roles'] ?? '');
|
||||
$this->start_date = $data['start_date'] ?? null;
|
||||
$this->end_date = $data['end_date'] ?? null;
|
||||
$this->enabled = isset($data['enabled']) ? (int) $data['enabled'] : 1;
|
||||
$this->condition_type = $data['condition_type'] ?? '';
|
||||
$this->condition_value = $data['condition_value'] ?? '';
|
||||
$this->action_type = $data['action_type'] ?? '';
|
||||
$this->action_value = $data['action_value'] ?? '';
|
||||
$this->created_at = $data['created_at'] ?? null;
|
||||
$this->updated_at = $data['updated_at'] ?? null;
|
||||
|
||||
if (empty($this->conditions) && !empty($this->condition_type)) {
|
||||
$this->conditions = [
|
||||
['type' => $this->condition_type, 'value' => $this->condition_value],
|
||||
];
|
||||
}
|
||||
|
||||
if (empty($this->actions) && !empty($this->action_type)) {
|
||||
$this->actions = [
|
||||
['type' => $this->action_type, 'value' => $this->action_value],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function parseJsonField($value) {
|
||||
@@ -88,16 +70,41 @@ class Rule {
|
||||
'actions' => wp_json_encode($this->actions),
|
||||
'priority' => $this->priority,
|
||||
'usage_limit' => $this->usage_limit,
|
||||
'usage_count' => $this->usage_count,
|
||||
'user_roles' => is_array($this->user_roles) ? implode(',', $this->user_roles) : $this->user_roles,
|
||||
'start_date' => $this->start_date,
|
||||
'end_date' => $this->end_date,
|
||||
'enabled' => $this->enabled,
|
||||
'condition_type' => $this->condition_type,
|
||||
'condition_value' => $this->condition_value,
|
||||
'action_type' => $this->action_type,
|
||||
'action_value' => $this->action_value,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if rule is active
|
||||
*/
|
||||
public function isActive() {
|
||||
if (!$this->enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = current_time('mysql');
|
||||
|
||||
if (!empty($this->start_date) && $now < $this->start_date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($this->end_date) && $now > $this->end_date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if usage limit reached
|
||||
*/
|
||||
public function hasReachedLimit() {
|
||||
return $this->usage_limit > 0 && $this->usage_count >= $this->usage_limit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,51 +2,80 @@
|
||||
namespace Sodino\Repositories;
|
||||
|
||||
use Sodino\Models\Rule;
|
||||
use Sodino\Core\Cache;
|
||||
|
||||
/**
|
||||
* Rule Repository
|
||||
*/
|
||||
class RuleRepository {
|
||||
private $table_name;
|
||||
private $cache;
|
||||
private $cache_group = 'rules';
|
||||
private $cache_duration = 3600;
|
||||
|
||||
public function __construct() {
|
||||
global $wpdb;
|
||||
$this->table_name = $wpdb->prefix . 'sodino_rules';
|
||||
$this->cache = Cache::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all rules
|
||||
*/
|
||||
public function getAll() {
|
||||
global $wpdb;
|
||||
$results = $wpdb->get_results("SELECT * FROM {$this->table_name} ORDER BY priority DESC, id ASC", ARRAY_A);
|
||||
$rules = [];
|
||||
foreach ($results as $result) {
|
||||
$rules[] = new Rule($result);
|
||||
}
|
||||
return $rules;
|
||||
return $this->cache->remember('all_rules', function() {
|
||||
global $wpdb;
|
||||
$results = $wpdb->get_results(
|
||||
"SELECT * FROM {$this->table_name} ORDER BY priority DESC, id ASC",
|
||||
ARRAY_A
|
||||
);
|
||||
$rules = [];
|
||||
foreach ($results as $result) {
|
||||
$rules[] = new Rule($result);
|
||||
}
|
||||
return $rules;
|
||||
}, $this->cache_duration, $this->cache_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rule by ID
|
||||
*/
|
||||
public function getById($id) {
|
||||
global $wpdb;
|
||||
$result = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$this->table_name} WHERE id = %d", $id), ARRAY_A);
|
||||
return $result ? new Rule($result) : null;
|
||||
return $this->cache->remember("rule_{$id}", function() use ($id) {
|
||||
global $wpdb;
|
||||
$result = $wpdb->get_row(
|
||||
$wpdb->prepare("SELECT * FROM {$this->table_name} WHERE id = %d", $id),
|
||||
ARRAY_A
|
||||
);
|
||||
return $result ? new Rule($result) : null;
|
||||
}, $this->cache_duration, $this->cache_group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enabled rules
|
||||
*/
|
||||
public function getEnabled() {
|
||||
global $wpdb;
|
||||
$results = $wpdb->get_results("SELECT * FROM {$this->table_name} WHERE enabled = 1 ORDER BY priority DESC, id ASC", ARRAY_A);
|
||||
$rules = [];
|
||||
foreach ($results as $result) {
|
||||
$rules[] = new Rule($result);
|
||||
}
|
||||
return $rules;
|
||||
return $this->cache->remember('enabled_rules', function() {
|
||||
global $wpdb;
|
||||
$now = current_time('mysql');
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM {$this->table_name}
|
||||
WHERE enabled = 1
|
||||
AND (start_date IS NULL OR start_date <= %s)
|
||||
AND (end_date IS NULL OR end_date >= %s)
|
||||
ORDER BY priority DESC, id ASC",
|
||||
$now,
|
||||
$now
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
$rules = [];
|
||||
foreach ($results as $result) {
|
||||
$rules[] = new Rule($result);
|
||||
}
|
||||
return $rules;
|
||||
}, $this->cache_duration, $this->cache_group);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,11 +88,16 @@ class RuleRepository {
|
||||
|
||||
if ($rule->id) {
|
||||
$wpdb->update($this->table_name, $data, ['id' => $rule->id]);
|
||||
return $rule->id;
|
||||
$id = $rule->id;
|
||||
} else {
|
||||
$wpdb->insert($this->table_name, $data);
|
||||
return $wpdb->insert_id;
|
||||
$id = $wpdb->insert_id;
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
$this->clearCache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,6 +105,31 @@ class RuleRepository {
|
||||
*/
|
||||
public function delete($id) {
|
||||
global $wpdb;
|
||||
return $wpdb->delete($this->table_name, ['id' => $id]);
|
||||
$result = $wpdb->delete($this->table_name, ['id' => $id]);
|
||||
|
||||
// Clear cache
|
||||
$this->clearCache();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment usage count
|
||||
*/
|
||||
public function incrementUsage($id) {
|
||||
global $wpdb;
|
||||
return $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE {$this->table_name} SET usage_count = usage_count + 1 WHERE id = %d",
|
||||
$id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache
|
||||
*/
|
||||
private function clearCache() {
|
||||
$this->cache->clearGroup($this->cache_group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,24 @@ namespace Sodino\Services;
|
||||
|
||||
use Sodino\Repositories\RuleRepository;
|
||||
use Sodino\Services\TrackingService;
|
||||
use Sodino\Core\Settings;
|
||||
use Sodino\Core\Cache;
|
||||
|
||||
class PricingService {
|
||||
private $ruleRepository;
|
||||
private $trackingService;
|
||||
private $rulesCache = null;
|
||||
private $settings;
|
||||
private $cache;
|
||||
|
||||
public function __construct(RuleRepository $ruleRepository, TrackingService $trackingService) {
|
||||
$this->ruleRepository = $ruleRepository;
|
||||
$this->trackingService = $trackingService;
|
||||
$this->settings = Settings::getInstance();
|
||||
$this->cache = Cache::getInstance();
|
||||
}
|
||||
|
||||
public function applyDynamicPricing($price, $product) {
|
||||
$settings = $this->getSettings();
|
||||
if (empty($settings['plugin_enabled']) || empty($settings['pricing_enabled'])) {
|
||||
if (!$this->settings->isPricingEnabled()) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
@@ -25,43 +29,58 @@ class PricingService {
|
||||
}
|
||||
|
||||
$price = $this->normalizePrice($price);
|
||||
if (!$settings['cart_pricing_enabled'] && is_cart()) {
|
||||
|
||||
if (!$this->settings->get('cart_pricing_enabled') && is_cart()) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
$originalPrice = $price;
|
||||
$rules = $this->getEnabledRules();
|
||||
$matchedRules = [];
|
||||
$rules = $this->getApplicableRules($product);
|
||||
|
||||
foreach ($rules as $rule) {
|
||||
if ($this->ruleMatches($rule, $product)) {
|
||||
$matchedRules[] = $rule;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($matchedRules)) {
|
||||
if (empty($rules)) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
if (!$settings['allow_multiple_rules']) {
|
||||
$chosenRule = $this->chooseRule($matchedRules, $price, $settings['strategy']);
|
||||
$matchedRules = $chosenRule ? [$chosenRule] : [];
|
||||
if (!$this->settings->get('allow_multiple_rules')) {
|
||||
$chosenRule = $this->chooseRule($rules, $price);
|
||||
$rules = $chosenRule ? [$chosenRule] : [];
|
||||
}
|
||||
|
||||
foreach ($matchedRules as $rule) {
|
||||
foreach ($rules as $rule) {
|
||||
$oldPrice = $price;
|
||||
$price = $this->applyActions($rule, $price);
|
||||
$price = $this->applyRuleActions($rule, $price);
|
||||
|
||||
if ($price < $oldPrice) {
|
||||
$this->trackingService->recordDiscountApplied($product, $oldPrice, $price, $rule->id);
|
||||
$this->ruleRepository->incrementUsage($rule->id);
|
||||
}
|
||||
}
|
||||
|
||||
$price = $this->enforceLimits($originalPrice, $price, $settings);
|
||||
$price = $this->enforceLimits($originalPrice, $price);
|
||||
|
||||
return max(0, $price);
|
||||
}
|
||||
|
||||
private function chooseRule(array $rules, $price, $strategy) {
|
||||
private function getApplicableRules($product) {
|
||||
$cache_key = 'applicable_rules_' . ($product ? $product->get_id() : 'all');
|
||||
|
||||
return $this->cache->remember($cache_key, function() use ($product) {
|
||||
$rules = $this->ruleRepository->getEnabled();
|
||||
$applicable = [];
|
||||
|
||||
foreach ($rules as $rule) {
|
||||
if ($this->ruleMatches($rule, $product)) {
|
||||
$applicable[] = $rule;
|
||||
}
|
||||
}
|
||||
|
||||
return $applicable;
|
||||
}, 300, 'pricing');
|
||||
}
|
||||
|
||||
private function chooseRule(array $rules, $price) {
|
||||
$strategy = $this->settings->get('strategy', 'priority');
|
||||
|
||||
if ($strategy === 'highest_discount') {
|
||||
usort($rules, function ($a, $b) use ($price) {
|
||||
return $this->estimateRuleDiscount($b, $price) <=> $this->estimateRuleDiscount($a, $price);
|
||||
@@ -79,44 +98,19 @@ class PricingService {
|
||||
return $rules[0] ?? null;
|
||||
}
|
||||
|
||||
private function getSettings() {
|
||||
$defaults = [
|
||||
'plugin_enabled' => 1,
|
||||
'pricing_enabled' => 1,
|
||||
'upsell_enabled' => 1,
|
||||
'allow_multiple_rules' => 0,
|
||||
'strategy' => 'priority',
|
||||
'max_discount_percent' => 100,
|
||||
'min_product_price' => 0,
|
||||
'ab_testing_enabled' => 0,
|
||||
'cart_pricing_enabled' => 1,
|
||||
'scheduled_campaigns_enabled' => 1,
|
||||
];
|
||||
|
||||
return wp_parse_args(get_option('sodino_settings', []), $defaults);
|
||||
}
|
||||
|
||||
private function getEnabledRules() {
|
||||
if ($this->rulesCache === null) {
|
||||
$this->rulesCache = $this->ruleRepository->getEnabled();
|
||||
}
|
||||
return $this->rulesCache;
|
||||
}
|
||||
|
||||
private function normalizePrice($price) {
|
||||
if ($price === '' || $price === null) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return floatval($price);
|
||||
}
|
||||
|
||||
private function ruleMatches($rule, $product = null) {
|
||||
if (!$rule->enabled) {
|
||||
if (!$rule->isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($rule->usage_limit > 0 && $this->trackingService->getRuleUsageCount($rule->id) >= $rule->usage_limit) {
|
||||
if ($rule->hasReachedLimit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,10 +120,6 @@ class PricingService {
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->isRuleActive($rule)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($rule->conditions)) {
|
||||
return true;
|
||||
}
|
||||
@@ -143,20 +133,6 @@ class PricingService {
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isRuleActive($rule) {
|
||||
$now = current_time('Y-m-d H:i:s');
|
||||
|
||||
if (!empty($rule->start_date) && $now < $rule->start_date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($rule->end_date) && $now > $rule->end_date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function evaluateCondition($condition, $product = null) {
|
||||
$type = $condition['type'] ?? '';
|
||||
$value = $condition['value'] ?? null;
|
||||
@@ -203,24 +179,21 @@ class PricingService {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getCartTotal() {
|
||||
if (!WC()->cart) {
|
||||
if (!function_exists('WC') || !WC()->cart) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return floatval(WC()->cart->get_cart_contents_total());
|
||||
return floatval(WC()->cart->get_subtotal());
|
||||
}
|
||||
|
||||
private function getCartItemCount() {
|
||||
if (!WC()->cart) {
|
||||
if (!function_exists('WC') || !WC()->cart) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return WC()->cart->get_cart_contents_count();
|
||||
return intval(WC()->cart->get_cart_contents_count());
|
||||
}
|
||||
|
||||
private function productHasCategory($product, $categories) {
|
||||
@@ -229,7 +202,11 @@ class PricingService {
|
||||
}
|
||||
|
||||
$product_cats = wp_get_post_terms($product->get_id(), 'product_cat', ['fields' => 'ids']);
|
||||
return (bool) array_intersect($product_cats, $categories);
|
||||
if (is_wp_error($product_cats)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) array_intersect($product_cats, array_map('intval', $categories));
|
||||
}
|
||||
|
||||
private function productIsInIds($product, $ids) {
|
||||
@@ -237,14 +214,13 @@ class PricingService {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($product->get_id(), $ids, true);
|
||||
return in_array($product->get_id(), array_map('intval', $ids), true);
|
||||
}
|
||||
|
||||
private function applyActions($rule, $price) {
|
||||
private function applyRuleActions($rule, $price) {
|
||||
foreach ($rule->actions as $action) {
|
||||
$price = $this->applyAction($action, $price);
|
||||
}
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
@@ -263,6 +239,8 @@ class PricingService {
|
||||
return $price;
|
||||
}
|
||||
return $price - $value;
|
||||
case 'set_price':
|
||||
return $value > 0 ? $value : $price;
|
||||
case 'free_shipping':
|
||||
return $price;
|
||||
default:
|
||||
@@ -270,14 +248,15 @@ class PricingService {
|
||||
}
|
||||
}
|
||||
|
||||
private function enforceLimits($originalPrice, $price, array $settings) {
|
||||
$minPrice = max(0, floatval($settings['min_product_price']));
|
||||
private function enforceLimits($originalPrice, $price) {
|
||||
$minPrice = max(0, floatval($this->settings->get('min_product_price', 0)));
|
||||
$price = max($price, $minPrice);
|
||||
|
||||
$maxDiscountPercent = floatval($settings['max_discount_percent']);
|
||||
$maxDiscountPercent = floatval($this->settings->get('max_discount_percent', 100));
|
||||
if ($maxDiscountPercent > 0 && $maxDiscountPercent < 100) {
|
||||
$limit = $originalPrice * ($maxDiscountPercent / 100);
|
||||
$price = max($originalPrice - $limit, $price);
|
||||
$maxDiscount = $originalPrice * ($maxDiscountPercent / 100);
|
||||
$minAllowedPrice = $originalPrice - $maxDiscount;
|
||||
$price = max($minAllowedPrice, $price);
|
||||
}
|
||||
|
||||
return $price;
|
||||
|
||||
Reference in New Issue
Block a user