Yii 1.1: How to implement magento cron on yii

3 followers

Hi Friends, First I would to say sorry but I am not very familiar to magento so after a 1 week spend I will implement the magneto cron functionality on yii I hope it's may be some helpful.

If I am not wrong magneto cron functionality work on read every xml file but in this tutorial I implement using database.

first everyone can set in your mind cron.php run every minutes

If you really develop a magneto cron functionality please follow the step..

1) First you can create the cron.php file on your root folder and paste the below code.

<?php
 
if (substr(phpversion(),0,1) != '6')
    error_reporting(E_ALL);
else
    error_reporting(E_ALL & ~E_STRICT);  
 
set_time_limit(0);
date_default_timezone_set('Europe/Oslo');
 
// change the following paths if necessary
//$yii=dirname(__FILE__).'/../yii/yii1.1.13/yii.php';
$yii=dirname(__FILE__).'/../yii/yii1.1.14/yii.php';
$blib=dirname(__FILE__).'/protected/blib.php';
$config=dirname(__FILE__).'/protected/config/cron.php';
// remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);
// specify how many levels of call stack should be shown in each log message
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);
require_once($blib);
require_once($yii);
Yii::createApplication('CConsoleApplication',$config)->run();
?>

In this file I Will change a JVWebApplication to CConsoleApplication because every cron will be run using command from.

2)Go to protected/commands directory and create the MainCommand.php file and paste the below code.

<?php
Yii::import('application.modules.admin.components.*');
class MainCommand extends CConsoleCommand {
 
 
    //protected $_resourceName;
 
 
    /**
     * Process cron queue
     * Geterate tasks schedule
     * Cleanup tasks schedule
     *
     */
 
     //SystemConfig::getValue('success_history_lifetime') = 200;
     //SystemConfig::getValue('failure_history_lifetime') = 200;
     //SystemConfig::getValueIndex('history_cleanup_every')= 10;
     //SystemConfig::getValue('missed_if_not_run_within') = 10;
     //SystemConfig::getValue('generate_schedules_every') = 15;
     //SystemConfig::getValue('schedule_ahead_for') = 50;
    public function run($args) {
        $schedules = $this->getPendingSchedules();
        $scheduleLifetime = SystemConfig::getValue('missed_if_not_run_within') * 60;
        $now = time();
        foreach ($schedules as $schedule) {
            foreach ($this->generate() as $key=>$value){
                $jobConfig = $value;
                if (!$jobConfig) {
                    continue;
                }
            }
            $time = strtotime($schedule->scheduled_at);
            if ($time > $now) {
                continue;
            }
 
            try {
 
                $errorStatus = VKcron::STATUS_ERROR;
                $errorMessage =('Unknown error.');
                if ($time < $now - $scheduleLifetime) {
                    $errorStatus = VKcron::STATUS_MISSED;
                    $schedule->status=$errorStatus;
                    $schedule->messages = 'Too late for the schedule.';
                }
                if (!$schedule->tryLockJob()) {
                    // another cron started this job intermittently, so skip it
                    continue;
                }
 
                $schedule->status = VkCron::STATUS_RUNNING;
                $schedule->executed_at = strftime('%Y-%m-%d %H:%M:%S', time());
 
 
                // this is command run..
                system('php cron.php '.$schedule->job_code, $retval);
 
 
 
                $schedule->status = VkCron::STATUS_SUCCESS;
                $schedule->finished_at = strftime('%Y-%m-%d %H:%M:%S', time());
 
 
 
 
            }catch (Exception $e) {
                $schedule->status = $errorStatus;
                $schedule->messages = $e->__toString();
            }
            $schedule->save();
        }
 
        $this->generate();
        $this->cleanup();
    }
 
    public function generate(){
 
 
        /**
         *  find a highest date 
         */
 
        $criteria = new CDbCriteria;
        $criteria->select = '*';
        $criteria->order = "scheduled_at desc";
        $criteria->limit = "1";
        $resultSet    =    VkCron::model()->find($criteria);
 
        if(!empty($lastRun1->scheduled_at)){
            $lastRun = $resultSet->scheduled_at;
        }else{
            $lastRun = date('Y-m-d H:i:s',time());
        }
        /*end*/
 
 
        if ($lastRun > time() - SystemConfig::getValue('generate_schedules_every') * 60) {
            return $this;
        }
 
        $exists = array();
        foreach($this->getPendingSchedules() as $value){
            $exists[$value->job_code.'/'.$value->scheduled_at] = 1;
            //$config[$value->job_code] = array('schedule'=>array('cron_expr'=>'*/5 * * * *'));
        }
        $vk_Web= VkWebshopSetting::model()->findAll('cron_exp IS NOT NULL');
        foreach ($vk_Web as $da){
            $va=explode(',',$da->display_value);
            if($va[1] == 'Yes'){
                $config[$da->cron_exp]=  array('schedule'=>array('cron_expr'=>$va[0]));
                $this->_generateJobs($config, $exists);
            }else{
                /*existing record set ignore*/
                $find_cron = VkCron::model()->findAll("job_code='".$da->cron_exp."'");
                $result = VkCron::model()->updateAll(array( 'status' => VkCron::STATUS_IGNORE,'messages'=>'This job has been ignore'), "job_code='".$da->cron_exp."'");
            }
 
 
        }
        return $config;
    }
 
 
    public function _generateJobs($config, $exists){
        $scheduleAheadFor = SystemConfig::getValue('schedule_ahead_for')*60;
        $schedule = new VkCron();
 
        foreach ($config as $jobCode => $jobConfig) {
            $cronExpr = null;
            if (empty($cronExpr) && $jobConfig['schedule']['cron_expr']) {
                $cronExpr = (string)$jobConfig['schedule']['cron_expr'];
 
            }
 
            if (!$cronExpr) {
                continue;
            }
 
            $now = time();
            $timeAhead = $now + $scheduleAheadFor;
            $schedule->setJobCode($jobCode);
            $schedule->setCronExpr($cronExpr);
            $schedule->setStatus(VKcron::STATUS_PENDING);
            for ($time = $now; $time < $timeAhead; $time += 60) {
                $ts = strftime('%Y-%m-%d %H:%M:00', $time);
                if (!empty($exists[$jobCode.'/'.$ts])) {
                    // already scheduled
                    continue;
                }
                if (!$schedule->trySchedule($time)) {
                    // time does not match cron expression
                    continue;
                }
 
                $model= new VkCron();
                $model->job_code=$schedule->getData('job_code');
                $model->status=$schedule->getData('status');
                $model->created_at=$schedule->getData('created_at');
                $model->scheduled_at=$schedule->getData('scheduled_at');
                $model->save(false);
 
            }
 
        }
        return $this;
 
    }
 
    public function getPendingSchedules()
    {
        $pendingSchedules=VkCron::model()->findAll("status='".VKcron::STATUS_PENDING."'");
        return $pendingSchedules;
    }
    public function cleanup(){
 
 
        /*find a lowest date */
            $criteria = new CDbCriteria;
            $criteria->select = '*';
            $criteria->order = "scheduled_at ASC";
            $criteria->limit = "1";
            $resultSet    =    VkCron::model()->find($criteria);
 
 
            if(!empty($resultSet->scheduled_at)){
                $lastCleanup = date('Y-m-d H:i:s', strtotime($resultSet->scheduled_at) - 100);
            }else{
                $lastCleanup = date('Y-m-d H:i:s',time());
            }
        /*end*/
 
 
        if ($lastCleanup > time() - SystemConfig::getValueIndex('history_cleanup_every')*60) {
            return $this;
        }
        $history=VkCron::model()->findAll("status IN ('".VKcron::STATUS_SUCCESS."', '".VKcron::STATUS_MISSED."','".VKcron::STATUS_ERROR."')");
        $historyLifetimes = array(
            VKcron::STATUS_SUCCESS => SystemConfig::getValue('success_history_lifetime')*60,
            VKcron::STATUS_MISSED => SystemConfig::getValue('failure_history_lifetime')*60,
            VKcron::STATUS_ERROR => SystemConfig::getValue('failure_history_lifetime')*60,
        );
        $now = time();
        foreach ($history as $record) {
            if (strtotime($record->executed_at) < $now - $historyLifetimes[$record->status]) {
                VkCron::model()->deleteAll();
            }
        }
 
    }
 
}
?>

So In this file I will created implement a all cron command run dynamically and create next scheduled generated.

My client requirement..

for ex:

If my command like import product 15 , import customer 2 etc.. so what excetlly this command

=> If I run this command on commnadForm

php cron.php import product 15

so fetch the all product data from magneto site to yii and insert the yii table.

3) whatever your command you can all command inserting on single table and set the default cron time.

CREATE TABLE `vk_webshop_setting` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `display_value` TEXT NULL,
    `cron_exp` TEXT NULL,
    `status` TINYINT(1) NULL DEFAULT '0' COMMENT 'Status',
    `position` INT(11) NULL DEFAULT '0' COMMENT 'Position',
    PRIMARY KEY (`id`),
 
    )
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
In this table insert the default cron time and command like 

for ex:

If I run import product 15 command scheduled every 5 minutes so in this table
INSERT INTO `vk_default_cron_setting` (`id`, `display_value`, `cron_exp`, `status`,`position`) VALUES (1,'*/5 * * * *,Yes', 'import notification 15', 1, ,0);
This is the my default cron table and next scheduled generate using this  */5 * * * * (display_value)

4) Next step you can create the next scheduled.

=>first create the table same as magento vk_cron table.
CREATE TABLE `vk_cron` (
        `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
        `job_code` VARCHAR(255) NULL DEFAULT NULL COMMENT 'Job Code',
        `status` ENUM('pending','running','sucess','missed','error','ignore') NOT NULL DEFAULT 'pending' COMMENT 'Status',
        `messages` TEXT NULL COMMENT 'Messages',
        `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Created At',
        `scheduled_at` TIMESTAMP NULL DEFAULT NULL COMMENT 'Scheduled At',
        `executed_at` TIMESTAMP NULL DEFAULT NULL COMMENT 'Executed At',
        `finished_at` TIMESTAMP NULL DEFAULT NULL COMMENT 'Finished At',
        PRIMARY KEY (`id`),
        INDEX `id` (`id`)
    )
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;

so create the VKCron.php model

<?php
 
Yii::import('application.models._base.BaseVkCron');
 
class VkCron extends BaseVkCron
{
    const STATUS_PENDING = 'pending';
    const STATUS_RUNNING = 'running';
    const STATUS_SUCCESS = 'sucess';
    const STATUS_MISSED = 'missed';
    const STATUS_ERROR = 'error';
    const STATUS_IGNORE = 'ignore';
 
    protected $_status = NULL;
    protected $_job_code = NULL;
    protected $_cron_expr_arr = NULL;
    protected $_created_at = NULL;
    protected $_executed_at = NULL;
 
 
 
    protected  $data = array();
 
    public static function model($className=__CLASS__) {
        return parent::model($className);
    }
 
 
    public function getData($key = null){
        if(trim($key)) {
            if(isset($this->data[$key])){
                return $this->data[$key];
            } else {
                return null;
            }
        }
        return $this->data;
 
    }
 
    public function setStatus($data)
    {
        $this->data['status'] = $data;
        return $this;
    }
 
    public function setJobCode($data)
    {
 
        $this->data['job_code'] = $data;
        return $this;
    }
 
 
    public function setCronExpr($data)
    {
        $this->data['cron_expr_arr'] = preg_split('#\s+#', $data, null, PREG_SPLIT_NO_EMPTY);
        if (sizeof($this->data['cron_expr_arr'])<5 || sizeof($this->data['cron_expr_arr'])>6) {
            echo "Invalid cron expression";
            Yii::app()->end();
        }
        return $this;
 
    }
 
    public function setCreatedAt($data)
    {
        $this->data['created_at'] = $data;
        return $this;
    }
    public function setScheduledAt($data)
    {
        $this->data['scheduled_at'] = $data;
        return $this;
    }
 
 
 
 
    public function trySchedule($time)
    {
        $data= date('Y-m-d H:i:s',$time);
 
        $e = $this->data['cron_expr_arr'];
        if (!$e || !$time) {
            return false;
        }
        if (!is_numeric($time)) {
            $time = strtotime($time);
        }
 
        $data1= explode(' ',$data);
 
        $tm = $data1[1];
        $currnet_time=explode(":",$tm);
 
        $unix = $data1[0];
        $currnt_date=explode("-",$unix);
 
        $day_of_week = date('N', strtotime(date('l')));
 
        $d = array(
                    'seconds'=>$currnet_time[2],
                    'minutes'=>ltrim($currnet_time[1],'0'),
                    'hours'=>ltrim($currnet_time[0],'0'),
                    'mday'=>ltrim($currnt_date[2],'0'),
                    'wday'=>$day_of_week,
                    'mon'=>ltrim($currnt_date[1],'0'),
                    'year'=>$currnt_date[0],
                    'yday'=>date('j',strtotime("-1 days")),
                    'weekday'=>date('l'),
                    'month'=>date('F'),
                    //'0'=>date('Y-m-d H:i:s',$time),
                    '0'=>$time,
        );
 
 
        $match = $this->matchCronExpression($e[0], $d['minutes'])
        && $this->matchCronExpression($e[1], $d['hours'])
        && $this->matchCronExpression($e[2], $d['mday'])
        && $this->matchCronExpression($e[3], $d['mon'])
        && $this->matchCronExpression($e[4], $d['wday']);
        if ($match) {
            $this->setCreatedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
            $this->setScheduledAt(strftime('%Y-%m-%d %H:%M', $time));
        }
        return $match;
    }
    public function matchCronExpression($expr, $num)
    {
        //handle ALL match
        if ($expr==='*') {
            return true;
        }
 
        // handle multiple options
        if (strpos($expr,',')!==false) {
            foreach (explode(',',$expr) as $e) {
                if ($this->matchCronExpression($e, $num)) {
                    return true;
                }
            }
            return false;
        }
 
        // handle modulus
        if (strpos($expr,'/')!==false) {
            $e = explode('/', $expr);
            if (sizeof($e)!==2) {
                echo "Invalid cron expression, expecting 'match/modulus':'.$expr.'";
                Yii::app()->end();
 
            }
            if (!is_numeric($e[1])) {
                echo "Invalid cron expression, expecting numeric modulus:'.$expr.'";
                Yii::app()->end();
            }
            $expr = $e[0];
            $mod = $e[1];
        } else {
            $mod = 1;
        }
 
        // handle all match by modulus
        if ($expr==='*') {
            $from = 0;
            $to = 60;
        }
        // handle range
        elseif (strpos($expr,'-')!==false) {
            $e = explode('-', $expr);
            if (sizeof($e)!==2) {
                echo "Invalid cron expression, expecting 'from-to' structure:'.$expr.'";
                Yii::app()->end();
 
            }
 
 
            $from = $this->getNumeric($e[0]);
            $to = $this->getNumeric($e[1]);
        }
        // handle regular token
        else {
            $from = $this->getNumeric($expr);
            $to = $from;
        }
 
        if ($from===false || $to===false) {
            echo "Invalid cron expression:'.$expr.'";
        }
        //echo $from .'to: '. $to .'Num: '.$num ; die;
        return ($num>=$from) && ($num<=$to) && ($num%$mod===0);
    }
 
 
    public function getNumeric($value)
    {
 
 
        static $data = array(
            'jan'=>1,
            'feb'=>2,
            'mar'=>3,
            'apr'=>4,
            'may'=>5,
            'jun'=>6,
            'jul'=>7,
            'aug'=>8,
            'sep'=>9,
            'oct'=>10,
            'nov'=>11,
            'dec'=>12,
 
            'sun'=>0,
            'mon'=>1,
            'tue'=>2,
            'wed'=>3,
            'thu'=>4,
            'fri'=>5,
            'sat'=>6,
        );
 
 
 
        if (is_numeric($value)) {
            return $value;
        }
 
        if (is_string($value)) {
            $value = strtolower(substr($value,0,3));
            if (isset($data[$value])) {
                return $data[$value];
            }
        }
 
        return false;
    }
 
    public function tryLockJob(){
        return $this->trySetJobStatusAtomic($this->id, VkCron::STATUS_RUNNING,VkCron::STATUS_PENDING);
    }
 
    public function trySetJobStatusAtomic($scheduleId, $newStatus, $currentStatus){
        $result = VkCron::model()->updateAll(array( 'status' => $newStatus), "id='".$scheduleId."' AND status='".$currentStatus."' ");
        if($result == 1){
            return true;
        }
        return false;
    }
 
 
 
 
 
 
}
when I was run command php cron.php main then next scheduled will be generated on db like
INSERT INTO `vk_cron` (`id`, `job_code`, `status`, `messages`, `created_at`, `scheduled_at`, `executed_at`, `finished_at`) VALUES (1, 'import productReview 15', 'pending', NULL, '2014-01-23 10:19:18', '2014-01-23 11:08:00', NULL, NULL);
In default magneto table set a 5 status and I will be add one more status ignore why I add this because when I was create the 
scheduled and after stop the cron so current execute scheduled will be set the ignore.

I hope it's easliy understand..

I will be added in feature.

Be the first person to leave a comment

Please to leave your comment.

Write new article