# acymailing/11.0.5/back/Classes/UserStatClass.php

AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress, version 11.0.5. 361 lines.

- Page: https://pluginprobe.com/plugins/acymailing/11.0.5/code/back/Classes/UserStatClass.php
- Raw: https://pluginprobe.com/plugins/acymailing/11.0.5/raw/back/Classes/UserStatClass.php
- Modified: 2026-09-03T09:36:36+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/acymailing/11.0.5/code/back/Classes/UserStatClass.php#L10-L20`.

```php
<?php

namespace AcyMailing\Classes;

use AcyMailing\Helpers\ScenarioHelper;
use AcyMailing\Core\AcymClass;

class UserStatClass extends AcymClass
{
    public function __construct()
    {
        parent::__construct();

        $this->table = 'user_stat';
    }

    public function save(object $element, bool $overrideSendDate = false): ?int
    {
        $userStat = get_object_vars($element);

        $column = [];
        $valueColumn = [];
        $columnName = acym_getColumns('user_stat');

        foreach ($userStat as $key => $value) {
            if (in_array($key, $columnName)) {
                $column[] = '`'.acym_secureDBColumn($key).'`';

                if ($key === 'tracking_sale') {
                    $valueColumn[] = strlen($value) === 0 ? 'NULL' : floatval($value);
                } else {
                    $valueColumn[] = acym_escapeDB($value);
                }
            }
        }

        $query = 'INSERT INTO #__acym_user_stat ('.implode(',', $column).') VALUE ('.implode(', ', $valueColumn).')';
        $onDuplicate = [];

        if (isset($userStat['statusSending'])) {
            $onDuplicate[] = empty($userStat['statusSending']) ? 'fail = fail + 1' : 'sent = sent + 1';
        }

        if ($overrideSendDate && !empty($userStat['send_date'])) {
            $onDuplicate[] = 'send_date = '.acym_escapeDB($userStat['send_date']);
        }

        if (!empty($userStat['open'])) {
            $onDuplicate[] = 'open = open + 1';
        }

        if (!empty($userStat['open_date'])) {
            $onDuplicate[] = 'open_date = '.acym_escapeDB($userStat['open_date']);
        }

        if (!empty($userStat['tracking_sale'])) {
            $onDuplicate[] = 'tracking_sale = '.floatval($userStat['tracking_sale']);
        }

        if (!empty($userStat['currency'])) {
            $onDuplicate[] = 'currency = '.acym_escapeDB($userStat['currency']);
        }

        if (!empty($userStat['unsubscribe'])) {
            $onDuplicate[] = 'unsubscribe = '.intval($userStat['unsubscribe']);
        }

        if (!empty($userStat['device'])) {
            $onDuplicate[] = 'device = '.acym_escapeDB($userStat['device']);
        }

        if (!empty($userStat['opened_with'])) {
            $onDuplicate[] = 'opened_with = '.acym_escapeDB($userStat['opened_with']);
        }

        if (!empty($onDuplicate)) {
            $query .= ' ON DUPLICATE KEY UPDATE ';
            $query .= implode(',', $onDuplicate);
        }

        return (int)acym_query($query);
    }

    public static function triggerActivity(string $event, int $userId, int $mailId): void
    {
        $payload = ['userId' => $userId, 'mailId' => $mailId];

        (new AutomationClass())->trigger($event, $payload);
        (new ScenarioHelper())->trigger($event, $payload);
    }

    public function getOneByMailAndUserId(int $mailId, int $userId): ?object
    {
        $userStat = acym_loadObject('SELECT * FROM #__acym_user_stat WHERE `mail_id` = '.intval($mailId).' AND `user_id` = '.intval($userId));

        return empty($userStat) ? null : $userStat;
    }

    public function getDetailedStatistics(array $options): array
    {
        $limit = $options['limit'] ?? 10;
        $offset = $options['offset'] ?? 0;
        $mailId = $options['mail_id'] ?? 0;

        return acym_loadObjectList(
            'SELECT user_stat.*, `user`.email
            FROM #__acym_user_stat AS user_stat
            JOIN #__acym_user AS `user` ON user_stat.user_id = `user`.id 
            WHERE `mail_id` = '.intval($mailId),
            '',
            $offset,
            $limit
        );
    }

    public function getAllUserStatByUserId(int $userId): array
    {
        return acym_loadObjectList(
            'SELECT userStat.*, mail.subject
            FROM #__acym_user_stat AS userStat 
            JOIN #__acym_mail AS mail ON mail.id = userStat.mail_id
            WHERE userStat.user_id = '.intval($userId)
        );
    }

    public function getDetailedStats(array $settings): array
    {
        $mailClass = new MailClass();

        $query = 'SELECT us.*, m.name, m.subject, m.parent_id, u.email, u.name AS username 
                    FROM #__acym_user_stat AS us
                    LEFT JOIN #__acym_user AS u ON us.user_id = u.id
                    INNER JOIN #__acym_mail AS m ON us.mail_id = m.id';
        $queryCount = 'SELECT COUNT(*) AS total FROM #__acym_user_stat as us
                        LEFT JOIN #__acym_user AS u ON us.user_id = u.id
                        INNER JOIN #__acym_mail AS m ON us.mail_id = m.id';
        $where = [];

        if (!empty($settings['mail_ids'])) {
            if (!is_array($settings['mail_ids'])) $settings['mail_ids'] = [$settings['mail_ids']];
            acym_arrayToInteger($settings['mail_ids']);
            $where[] = 'us.mail_id IN ('.implode(',', $settings['mail_ids']).')';
        }

        if (!empty($settings['search'])) {
            $searchTerms = acym_escapeDB('%'.$settings['search'].'%');
            $where[] = 'm.name LIKE '.$searchTerms.' OR u.email LIKE '.$searchTerms.' OR u.name LIKE '.$searchTerms;
        }

        if (!empty($where)) {
            $query .= ' WHERE ('.implode(') AND (', $where).')';
            $queryCount .= ' WHERE ('.implode(') AND (', $where).')';
        }

        if (!empty($settings['ordering']) && !empty($settings['ordering_sort_order'])) {
            if (in_array($settings['ordering'], ['email', 'name'])) {
                $table = 'u';
            } elseif ($settings['ordering'] === 'subject') {
                $table = 'm';
            } else {
                $table = 'us';
            }
            $query .= ' ORDER BY '.$table.'.'.acym_secureDBColumn($settings['ordering']).' '.acym_secureDBColumn(strtoupper($settings['ordering_sort_order'])).', us.user_id ASC';
        }

        $mails = acym_loadObjectList($query, '', $settings['offset'], $settings['detailedStatsPerPage']);

        if (!empty($mails)) {
            if (acym_isTrackingSalesActive()) {
                $mailIds = [];
                foreach ($mails as $mail) {
                    if (!in_array($mail->mail_id, $mailIds)) $mailIds[] = $mail->mail_id;
                }

                $trackingSales = [];
                $trackingSalesDB = acym_loadObjectList(
                    'SELECT SUM(tracking_sale) as sale, currency, mail_id, user_id FROM #__acym_user_stat WHERE mail_id IN ('.implode(
                        ',',
                        $mailIds
                    ).')  AND currency IS NOT NULL GROUP BY user_id, mail_id'
                );
                foreach ($trackingSalesDB as $oneMailTrackSales) {
                    $trackingSales[$oneMailTrackSales->mail_id.'-'.$oneMailTrackSales->user_id] = $oneMailTrackSales;
                }
            }

            $totalClicksForMails = [];
            $campaignsForMails = [];
            foreach ($mails as $key => $mail) {
                // Add tracked income amount
                if (!empty($trackingSales[$mail->mail_id.'-'.$mail->user_id])) {
                    acym_trigger('getCurrency', [&$mails[$key]->currency]);
                    $mails[$key]->sales = $trackingSales[$mail->mail_id.'-'.$mail->user_id]->sale;
                }

                // Add total clicks
                if (!isset($totalClicksForMails[$mail->mail_id])) {
                    $totalClicksForMails[$mail->mail_id] = acym_loadObjectList(
                        'SELECT SUM(click) AS nbClicks, user_id
                        FROM #__acym_url_click 
                        WHERE mail_id = '.intval($mail->mail_id).' 
                        GROUP BY user_id',
                        'user_id'
                    );
                }
                $mails[$key]->total_click = empty($totalClicksForMails[$mail->mail_id][$mail->user_id]) ? 0 : $totalClicksForMails[$mail->mail_id][$mail->user_id]->nbClicks;

                // Handle multilingual
                $mainMailId = empty($mail->parent_id) ? $mail->mail_id : $mail->parent_id;
                if (!isset($campaignsForMails[$mainMailId])) {
                    $campaignsForMails[$mainMailId] = acym_loadObject('SELECT id AS campaign_id, parent_id FROM #__acym_campaign WHERE mail_id = '.intval($mainMailId));
                }

                $mails[$key]->campaign_id = empty($campaignsForMails[$mainMailId]) ? 0 : $campaignsForMails[$mainMailId]->campaign_id;
                $mails[$key]->parent_id = empty($campaignsForMails[$mainMailId]) ? 0 : $campaignsForMails[$mainMailId]->parent_id;
            }
        }

        return [
            'detailed_stats' => $mailClass->decode($mails),
            'total' => acym_loadObject($queryCount),
        ];
    }

    public function getOpenTimeStats(array $mailIds): array
    {
        acym_arrayToInteger($mailIds);
        $specificMailsCondition = empty($mailIds) ? '' : ' AND mail_id in ('.implode(',', $mailIds).')';
        $offsetSeconds = acym_getTimeOffsetCMS();

        return [
            'total_open' => acym_loadResult(
                'SELECT SUM(`open`)
                FROM #__acym_user_stat
                WHERE open_date IS NOT NULL '.$specificMailsCondition
            ),
            'stats' => acym_loadObjectList(
                'SELECT
                    SUM(`open`) AS `open_total`,
                    DATE_FORMAT(DATE_ADD(open_date, INTERVAL '.$offsetSeconds.' SECOND), "%w") AS `day`,
                    FORMAT(CONVERT(DATE_FORMAT(DATE_ADD(open_date, INTERVAL '.$offsetSeconds.' SECOND), "%H"), SIGNED INTEGER) / 3, 0) AS `hour`,
                    CONCAT(
                        DATE_FORMAT(DATE_ADD(open_date, INTERVAL '.$offsetSeconds.' SECOND), "%w"),
                        "_",
                        FORMAT(CONVERT(DATE_FORMAT(DATE_ADD(open_date, INTERVAL '.$offsetSeconds.' SECOND), "%H"), SIGNED INTEGER) / 3, 0)
                    ) AS `date_id`
                FROM `#__acym_user_stat`
                WHERE open_date IS NOT NULL '.$specificMailsCondition.'
                GROUP BY date_id',
                'date_id'
            ),
        ];
    }

    public function getDefaultStat(): array
    {
        $percentageRemaining = 100;
        $stats = [];

        for ($day = 0; $day < 7; $day++) {
            $stats[$day] = [];
            for ($hour = 0; $hour < 8; $hour++) {
                $stats[$day][$hour] = $this->getRandomStatOpenTime($percentageRemaining, $hour);
            }
        }

        return $stats;
    }

    public function getOpenSourcesStats(array $mailIds = []): array
    {
        acym_arrayToInteger($mailIds);

        $query = 'SELECT opened_with, COUNT(*) AS number FROM #__acym_user_stat WHERE `open` > 0';
        if (!empty($mailIds)) {
            $query .= ' AND mail_id IN ('.implode(',', $mailIds).')';
        }
        $query .= ' GROUP BY opened_with';

        return acym_loadObjectList($query);
    }

    public function deleteDetailedStatsPeriod(): array
    {
        $shouldClearExpiredStats = $this->config->get('delete_stats_enabled', 0);
        if (empty($shouldClearExpiredStats)) {
            return [];
        }

        $deleteAfterXSeconds = $this->config->get('delete_stats', 86400 * 360);
        if (empty($deleteAfterXSeconds)) {
            return [];
        }

        $date = acym_date(time() - $deleteAfterXSeconds, 'Y-m-d H:i', false);

        $queries = [
            '#__acym_user_stat' => 'DELETE FROM #__acym_user_stat WHERE send_date < '.acym_escapeDB($date),
            '#__acym_url_click' => 'DELETE FROM #__acym_url_click WHERE date_click < '.acym_escapeDB($date),
        ];

        $messages = [];
        foreach ($queries as $table => $query) {
            try {
                $status = acym_query($query);
                if (!empty($status)) {
                    $messages[] = acym_translationSprintf('ACYM_DELETE_X_ROWS_TABLE_X', $status, acym_translation('ACYM_USER_DETAILED_STATS').' ('.$table.')');
                }
            } catch (\Exception $e) {
                $messages[] = $e->getMessage();
            }
        }

        if (empty($messages)) {
            return [];
        }

        return ['message' => implode("\r\n", $messages)];
    }

    private function getRandomStatOpenTime(int &$percentageRemaining, int $hour): int
    {
        if (empty($percentageRemaining)) {
            return 0;
        }

        $randoms = [
            0 => [0, 1],
            1 => [0, 2],
            2 => [1, 2],
            3 => [1, 3],
            4 => [3, 5],
            5 => [1, 3],
            6 => [1, 2],
            7 => [0, 2],
        ];

        $percentage = acym_rand($randoms[$hour][0], $randoms[$hour][1]);

        if ($percentageRemaining - $percentage < 0) {
            return 0;
        }

        $percentageRemaining -= $percentage;

        return $percentage;
    }

    public function hasUserReceivedMail(int $mailId, int $userId): bool
    {
        $sentToRecipient = acym_loadResult(
            'SELECT COUNT(*)
            FROM #__acym_user_stat AS userStat
            WHERE userStat.mail_id = '.intval($mailId).'
                AND userStat.user_id = '.intval($userId)
        );

        return !empty($sentToRecipient);
    }
}

```
