You are viewing revision #9 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.
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.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.