1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Prevent Duplicate

Discussion in 'XenForo Development Discussions' started by Robust, Nov 21, 2015.

  1. Robust

    Robust Well-Known Member

    Hey,

    So I'm using deferred to remove a string from a varbinary storing a stringlist of "tasks completed" when a "task" is deleted, similar to XenForo's secondary user groups system.

    I'm fetching users and it appears as if the same user is constantly fetched again and won't stop until application error. How do I prevent duplicate entries?
     
  2. Jake B.

    Jake B. Well-Known Member

    could you provide your deferred class? Sounds like you aren't getting the list of user IDs first then getting X users from that list per batch, but instead are just selecting X users on each batch so it keeps hitting the same users, but a look at your Deferred class will help :)
     
  3. Robust

    Robust Well-Known Member

    Called in DataWriter:
    Code:
        protected function _postDelete()
        {
            $taskId = $this->get('task_id');
            $points = $this->get('added_points');
    
            $data = array('taskId' => $taskId, 'points' => $points);
            XenForo_Application::defer('Apantic_Tasks_Deferred_TaskDelete', $data, "taskDelete_$taskId", true);
        }
    
    Deferred:
    Code:
    class Apantic_Tasks_Deferred_TaskDelete extends XenForo_Deferred_Abstract
    {
        public function canTriggerManually()
        {
            return false;
        }
    
        public function execute(array $deferred, array $data, $targetRunTime, &$status)
        {
            $data = array_merge(array(
                'taskId' => 0,
                'count' => 0
            ), $data);
    
            if (!$data['taskId'])
            {
                return false;
            }
    
            $s = microtime(true);
    
            /* @var $progressModel Apantic_Tasks_Model_Tasks */
            $progressModel = XenForo_Model::create('Apantic_Tasks_Model_Tasks');
    
            $limit = 100;
    
            do
            {
                $results = $progressModel->removeTaskFromUserIdsWithTask($data['taskId'], $limit, $data['count']);
                if(empty($results))
                {
                    return false;
                }
    
                $data['count'] += count($results);
            }
            while ($targetRunTime && microtime(true) - $s < $targetRunTime);
    
            $actionPhrase = new XenForo_Phrase('deleting');
            $typePhrase = new XenForo_Phrase('user_group');
            $status = sprintf('%s... %s (%s)', $actionPhrase, $typePhrase, XenForo_Locale::numberFormat($data['count']));
    
            return $data;
        }
    }
    Model functions:

    Code:
        public function setCompletedTasks($user, array $taskIds)
        {
            $db = $this->_getDb();
            $tasks = (!empty($user['aupp_completed_tasks']) ? implode(',', array_merge(explode(',', $user['aupp_completed_tasks']), $taskIds)) : implode(',', $taskIds));
            $db->query('
                UPDATE xf_user
                SET aupp_completed_tasks = ?
                WHERE user_id = ?
            ', array($tasks, $user['user_id']));
        }
    
        public function getCompletedTasks($user)
        {
            return explode(',', $user['aupp_completed_tasks']);
        }
    
        public function removeTaskFromUserIdsWithTask($taskId, $limit = 0, $offset = 0)
        {
            $affectedUserIds = array();
    
            $data = $this->_getDb()->fetchAll($this->limitQueryResults(
                '
                    SELECT user_id, aupp_completed_tasks
                    FROM xf_user
                    WHERE aupp_completed_tasks <> \'\'
                    ORDER BY user_id
                ', $limit, $offset
            ));
    
            foreach($data as $user)
            {
                $userTasks = $this->getCompletedTasks($user);
    
                foreach($userTasks AS $key => $value)
                {
                    if($value == $taskId)
                    {
                        unset($userTasks[$key]);
                        $this->setCompletedTasks($user, $userTasks);
                        $affectedUserIds[] = $user['user_id'];
                    }
                }
            }
    
            return $affectedUserIds;
        }
    aupp_completed_tasks table, varbinary(255) not null

    As it looks, I really have no idea what I'm doing here.
     
  4. Chris D

    Chris D XenForo Developer Staff Member

    I suspect it is your offset approach on the user fetching.

    Rather than using an offset instead keep track of the last user ID you worked on. Then your query can be equivalent to "WHERE user_id > $lastId". That way you can avoid passing in an offset, which I must add, doesn't scale well in queries and should be avoided where possible.
     
  5. Robust

    Robust Well-Known Member

    I see - is my method of the deferred a bit clumsy by any chance?
     

Share This Page