Files
2026-09-15 10:35:56 +08:00

134 lines
7.2 KiB
PHP

<?php
declare(strict_types=1);
namespace Esun\Reward\Model;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Store\Model\ScopeInterface;
class Config
{
public function __construct(
private readonly ScopeConfigInterface $scopeConfig,
private readonly Json $serializer
) {}
public function isEnabled(?int $storeId = null): bool { return $this->flag('general/enabled', $storeId); }
public function registrationPoints(?int $storeId = null): int { return (int)$this->earningRule('registration', $storeId)['points']; }
public function firstAddressPoints(?int $storeId = null): int { return (int)$this->earningRule('first_shipping_address', $storeId)['points']; }
public function orderRate(?int $storeId = null): float { return (float)$this->earningRule('order_earned', $storeId)['points']; }
public function reviewTextPoints(?int $storeId = null): int { return (int)$this->earningRule('review_text', $storeId)['points']; }
public function reviewMediaPoints(?int $storeId = null): int { return (int)$this->earningRule('review_media', $storeId)['points']; }
public function socialPoints(?int $storeId = null): int { return (int)$this->earningRule('social_share', $storeId)['points']; }
public function redemptionRate(?int $storeId = null): int { return max(1, $this->int('spending/points_per_dollar', $storeId)); }
public function maxCheckoutPoints(?int $storeId = null): int { return $this->int('spending/max_checkout_points', $storeId); }
public function reviewEligibility(?int $storeId = null): string { return (string)$this->value('review/eligibility', $storeId); }
public function silverThreshold(?int $storeId = null): float { return $this->float('tiers/silver_threshold', $storeId); }
public function silverMultiplier(?int $storeId = null): float { return $this->float('tiers/silver_multiplier', $storeId); }
public function goldThreshold(?int $storeId = null): float { return $this->float('tiers/gold_threshold', $storeId); }
public function goldMultiplier(?int $storeId = null): float { return $this->float('tiers/gold_multiplier', $storeId); }
/** @return array{code: string, points: float, name: string, reason: string} */
public function earningRule(string $code, ?int $storeId = null): array
{
$rules = $this->earningRules($storeId);
return $rules[$code] ?? ['code' => $code, 'points' => 0.0, 'name' => ucwords(str_replace('_', ' ', $code)), 'reason' => ''];
}
/** @return array<string, array{code: string, points: float, name: string, reason: string}> */
public function earningRules(?int $storeId = null): array
{
$defaults = [
'registration' => ['points' => (float)$this->int('earning/registration_points', $storeId), 'name' => 'Account Registration', 'reason' => 'Account registration reward'],
'first_shipping_address' => ['points' => (float)$this->int('earning/first_address_points', $storeId), 'name' => 'First Shipping Address', 'reason' => 'First saved shipping address reward'],
'order_earned' => ['points' => $this->float('earning/points_per_dollar', $storeId), 'name' => 'Order Reward', 'reason' => 'Points earned from order %1'],
'review_text' => ['points' => (float)$this->int('earning/review_text_points', $storeId), 'name' => 'Product Review', 'reason' => 'Approved product review reward'],
'review_media' => ['points' => (float)$this->int('earning/review_media_points', $storeId), 'name' => 'Product Review with Media', 'reason' => 'Approved product review reward'],
'social_share' => ['points' => (float)$this->int('earning/social_share_points', $storeId), 'name' => 'Social Share', 'reason' => 'Social share reward'],
];
foreach ($defaults as $code => &$rule) {
$rule['code'] = $code;
}
unset($rule);
$value = $this->value('earning/items', $storeId);
try {
$items = is_array($value) ? $value : (empty($value) ? [] : $this->serializer->unserialize((string)$value));
} catch (\InvalidArgumentException) {
$items = [];
}
if (!is_array($items) || !$items) {
return $defaults;
}
$rules = [];
foreach ($items as $item) {
if (!is_array($item)) {
continue;
}
$code = strtolower(trim((string)($item['code'] ?? '')));
$name = trim((string)($item['name'] ?? ''));
if ($code === '' || $name === '') {
continue;
}
$rules[$code] = [
'code' => $code,
'points' => max(0.0, (float)($item['points'] ?? 0)),
'name' => $name,
'reason' => trim((string)($item['reason'] ?? '')),
];
}
return $rules ?: $defaults;
}
/** @return array<int, array{code: string, label: string, threshold: float, multiplier: float}> */
public function tiers(?int $storeId = null): array
{
$value = $this->value('tiers/items', $storeId);
try {
$items = is_array($value) ? $value : (empty($value) ? [] : $this->serializer->unserialize((string)$value));
} catch (\InvalidArgumentException) {
$items = [];
}
if (!is_array($items)) {
$items = [];
}
$tiers = [];
foreach ($items as $item) {
if (!is_array($item)) {
continue;
}
$label = trim((string)($item['label'] ?? ''));
$threshold = max(0.0, (float)($item['threshold'] ?? 0));
$multiplier = (float)($item['multiplier'] ?? 0);
if ($label === '' || $multiplier <= 0) {
continue;
}
$tiers[] = [
'code' => strtolower((string)preg_replace('/[^a-z0-9]+/i', '_', $label)),
'label' => $label,
'threshold' => $threshold,
'multiplier' => $multiplier,
];
}
if (!$tiers) {
$tiers = [
['code' => 'bronze', 'label' => 'Bronze Maker', 'threshold' => 0.0, 'multiplier' => 1.0],
['code' => 'silver', 'label' => 'Silver Maker', 'threshold' => $this->silverThreshold($storeId), 'multiplier' => $this->silverMultiplier($storeId)],
['code' => 'gold', 'label' => 'Gold Maker', 'threshold' => $this->goldThreshold($storeId), 'multiplier' => $this->goldMultiplier($storeId)],
];
}
usort($tiers, static fn(array $left, array $right): int => $left['threshold'] <=> $right['threshold']);
return $tiers;
}
private function value(string $path, ?int $storeId): mixed { return $this->scopeConfig->getValue('esun_reward/' . $path, ScopeInterface::SCOPE_STORE, $storeId); }
private function int(string $path, ?int $storeId): int { return max(0, (int)$this->value($path, $storeId)); }
private function float(string $path, ?int $storeId): float { return max(0.0, (float)$this->value($path, $storeId)); }
private function flag(string $path, ?int $storeId): bool { return $this->scopeConfig->isSetFlag('esun_reward/' . $path, ScopeInterface::SCOPE_STORE, $storeId); }
}