diff($startDate); $days = $diff->days; if ($days <= 91) { return 'daily'; } elseif ($days <= 365) { return 'monthly'; } else { return 'yearly'; } } public static function processGroup($startDate, $endDate, $groupKey = null) { if (!$groupKey || $groupKey === 'default') { $groupKey = static::defineGroupKey($startDate, $endDate); } switch ($groupKey) { case 'yearly': $format = '%Y'; break; case 'monthly': $format = '%Y-%m'; break; default: $format = '%Y-%m-%d'; break; } $groupBy = "DATE_FORMAT(o.created_at, '{$format}')"; $selection = "{$groupBy} AS `group`, YEAR(o.created_at) AS year"; return [ 'key' => $groupKey, 'by' => $groupBy, 'field' => $selection, ]; } public static function processFilters($params = []) { $storeMode = (new StoreSettings())->get('order_mode') ?: 'live'; $mode = $params['filterMode'] ?? $storeMode; $filters = [ 'payment_status' => $params['paymentStatus'] ?? null, 'status' => $params['orderStatus'] ?? null, 'currency' => $params['currency'] ?? null, 'mode' => $mode, ]; return array_filter($filters); } public static function processRequest($params = []): array { $storeMode = (new StoreSettings())->get('order_mode') ?: 'live'; $mode = $params['filterMode'] ?? $storeMode; $filters = [ 'payment_status' => Arr::get($params, 'paymentStatus'), 'status' => Arr::get($params, 'orderStatus'), 'currency' => Arr::get($params, 'currency'), 'mode' => $mode, 'type' => array_filter(Arr::get($params, 'orderTypes', [])), ]; $groupKey = Arr::get($params, 'groupKey', 'daily'); return [ 'filters' => array_filter($filters), 'startDate' => Arr::get($params, 'startDate'), 'endDate' => Arr::get($params, 'endDate'), 'groupKey' => $groupKey, ]; } public static function processParams($params = [], $additional = []): array { $params = static::sanitizeParams($params); if (!App::isProActive()) { $startDate = (new DateTime())->subMonth()->startOfDay(); $endDate = (new DateTime())->endOfDay(); } else { $startDate = (new DateTime(Arr::get($params, 'startDate')))->startOfDay(); $endDate = (new DateTime(Arr::get($params, 'endDate')))->endOfDay(); } $compareType = Arr::get($params, 'compareType'); $compareDate = Arr::get($params, 'compareDate'); $comparePeriod = null; if ($compareType && $compareDate) { $comparePeriod = static::getCompareRange( $compareType, ['startDate' => $startDate, 'endDate' => $endDate], $compareDate ); } $attributes = [ 'startDate' => $startDate, 'endDate' => $endDate, 'groupKey' => Arr::get($params, 'groupKey'), 'currency' => Arr::get($params, 'currency'), 'paymentMode' => Arr::get($params, 'filterMode'), 'variationIds' => Arr::get($params, 'variation_ids', []), 'comparePeriod' => $comparePeriod ]; foreach ($additional as $key) { $value = Arr::get($params, $key); if ($key === 'orderStatus') { if (!$value || in_array('all', $value)) { $value = [Status::ORDER_ON_HOLD, Status::ORDER_FAILED]; } } elseif ($key === 'paymentStatus') { $value = Status::getReportStatuses(); } elseif ($key === 'subscriptionType') { $value = $value ?: Status::ORDER_TYPE_SUBSCRIPTION; } $attributes[$key] = $value; } return $attributes; } /** * Whitelist a groupKey value used to build raw SQL (SELECT/GROUP BY clauses) * so an unrecognized or missing value never reaches the query builder. * * @param mixed $value * @return string */ public static function sanitizeGroupKey($value) { $acceptedValues = ['billing_country', 'shipping_country', 'payment_method', 'payment_status', 'default', 'daily', 'monthly', 'yearly']; return in_array($value, $acceptedValues) ? $value : 'payment_method'; } protected static function sanitizeParams($params) { $rules = [ 'startDate' => 'sanitize_text_field', 'endDate' => 'sanitize_text_field', 'compareType' => 'sanitize_text_field', 'compareDate' => 'sanitize_text_field', 'groupKey' => function ($value) { return static::sanitizeGroupKey($value); }, 'currency' => 'sanitize_text_field', 'filterMode' => 'sanitize_text_field', 'storeMode' => 'sanitize_text_field', 'variation_ids.*' => 'intval', 'customDays' => 'intval', 'subscriptionType' => 'sanitize_text_field', 'orderStatus.*' => 'sanitize_text_field', 'orderTypes.*' => 'sanitize_text_field', 'filter_type' => 'sanitize_text_field', ]; /** * Report params whose shape this plugin does not own. * * `advanced_filters` deliberately has no rule above. Its payload describes * filter conditions, and only whoever consumes it knows what shape is * valid — so only they can sanitize it without mangling it (running * sanitize_text_field() over a JSON blob eats everything after the first * '<'). A consumer pushes its own rule in here, and Sanitizer::sanitize() * leaves any key with no rule untouched. * * @param array $rules Sanitization rules, keyed like $params. * @param array $params The raw, unsanitized params. */ $rules = apply_filters('fluent_cart/report/sanitize_params_rules', $rules, $params); return Sanitizer::sanitize($params, is_array($rules) ? $rules : []); } /** * @param string $type * @param array $compareRange * @param array $currentRange * @return array|\DateTime[]|false * @throws \Exception */ public static function getCompareRange($type, $currentRange, $compareDate = null) { $currentStartDate = $currentRange['startDate']; $currentendDate = $currentRange['endDate']; $diffDays = $currentendDate->diff($currentStartDate)->days; if ($type == 'previous_period') { return [ $currentStartDate->copy()->subDays($diffDays + 1), $currentStartDate->copy()->subDays(1)->endOfDay() ]; } else if ($type == 'previous_month') { $fromDate = $currentStartDate->copy()->subMonths(1); return [ $fromDate->copy(), $fromDate->addDays($diffDays)->endOfDay() ]; } else if ($type == 'previous_quarter') { $fromDate = $currentStartDate->copy()->subMonths(3); return [ $fromDate->copy()->startOfMonth(), $fromDate->addDays($diffDays)->endOfDay() ]; } else if ($type == 'previous_year') { $fromDate = $currentStartDate->copy()->subYears(1); return [ $fromDate->copy(), $fromDate->addDays($diffDays)->endOfDay() ]; } else if ($type == 'custom' && $compareDate) { $compareDate = (new DateTime($compareDate))->startOfDay(); return [ $compareDate->copy(), $compareDate->addDays($diffDays)->endOfDay(), ]; } else { return false; } } }