Magento Configurable Cron

Clock

Cron in Magento is fairly easy to configure. A few lines in config.xml, a method and it’s done. But what happens if you want to spice it up a bit and create a schedule-configurable cron? Fortunately for us, Magento has that already included in the system and it’s pretty easy to implement.

Cron itself is more or less straightforward. As seen in the code below, it is defined in a config.xml file with two main parts: schedule and a method to be ran. A sample below shows a cron job named “my_cron” that runs “doSomething” method inside the observer file every five minutes. If you’re not familiar with the cron schedule format, there’s a bunch of the articles and cron generators on the internet.

<config>
    <crontab>
        <jobs>
            <my_cron>
                <schedule>
                    <cron_expr>*/5 * * * *</cron_expr>
                </schedule>
                <run>
                    <model>mymodule/observer::doSomething</model>
                </run>
            </my_cron>
        </jobs>
    </crontab>
</config>

The thing about this is that the schedule cannot be configured…yet. Inside the Mage_Cron_Model_Observer class, which generates cron jobs, there’s a “generate” method that also checks for the values inside core_config_data table in the same way as it does for config.xml files. Having that in mind, we could save the path to the cron schedule and set any (valid) value we want. So let’s get to work!

Admin configuration

As we want to have schedule-configurable cron we have to have an admin configuration for that. As you can make it whatever and however you want, I’m not going to do the miracles here but I’ll use what Magento already offers to us.

Inside the system.xml will be time (hour, minute and second) and frequency selectors (daily, weekly and monthly) that already come packed within Magento. All the coding that has to be done is to create our own backend model that will format and save the values to path we want. In this case  it’s will be in the mymodule/adminhtml_system_config_backend_mymodel_cron.

<config>
    <sections>
        <catalog>
            <groups>
                <configurable_cron translate="label">
                    <label>Cron Schedule</label>
                    <sort_order>100</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>0</show_in_website>
                    <show_in_store>0</show_in_store>
                    <fields>
                        <time translate="label">
                            <label>Start Time</label>
                            <frontend_type>time</frontend_type>
                            <sort_order>10</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>0</show_in_website>
                            <show_in_store>0</show_in_store>
                        </time>
                        <frequency translate="label">
                            <label>Frequency</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_cron_frequency</source_model>
                            <backend_model>mymodule/adminhtml_system_config_backend_mymodel_cron</backend_model>
                            <sort_order>20</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>0</show_in_website>
                            <show_in_store>0</show_in_store>
                        </frequency>
                    </fields>
                </configurable_cron>
            </groups>
        </catalog>
    </sections>
</config>

Backend model

The frontend types in the admin configuration don’t save the inputs in the way the cron generator can read them. Because of that we have to have a backend model that fill format those inputs the way we want.

From the sample code above on how the regular cron job is set in the config.xml, the same paths should be used in the core_config_data table that we’re going to deal with. From the code above, the path to our schedule would be “crontab/jobs/my_cron/schedule/cron_expr”. The code below formats the inputs and saves the value to the table.

class Inchoo_MyModule_Model_Adminhtml_System_Config_Backend_MyModel_Cron extends Mage_Core_Model_Config_Data
{
    const CRON_STRING_PATH = 'crontab/jobs/my_cron/schedule/cron_expr';
 
    protected function _afterSave()
    {
        $time = $this->getData('groups/configurable_cron/fields/time/value');
        $frequency = $this->getValue();
 
        $frequencyWeekly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_WEEKLY;
        $frequencyMonthly = Mage_Adminhtml_Model_System_Config_Source_Cron_Frequency::CRON_MONTHLY;
 
        $cronExprArray = array(
            intval($time[1]),                                   # Minute
            intval($time[0]),                                   # Hour
            ($frequency == $frequencyMonthly) ? '1' : '*',      # Day of the Month
            '*',                                                # Month of the Year
            ($frequency == $frequencyWeekly) ? '1' : '*',       # Day of the Week
        );
        $cronExprString = join(' ', $cronExprArray);
 
        try {
            Mage::getModel('core/config_data')
                ->load(self::CRON_STRING_PATH, 'path')
                ->setValue($cronExprString)
                ->setPath(self::CRON_STRING_PATH)
                ->save();
        }
        catch (Exception $e) {
            throw new Exception(Mage::helper('cron')->__('Unable to save the cron expression.'));
 
        }
    }
}

Cron generator goes through the cron jobs and takes only the ones with the schedule defined. To have the schedule read from the config table, the schedule node has to be removed from the config.xml and only have the model defined, as shown below.

<config>
    <crontab>
        <jobs>
            <my_cron>
                <run>
                    <model>mymodule/observer::doSomething</model>
                </run>
            </my_cron>
        </jobs>
    </crontab>
</config>

What happens in the background? Cron generator runs through the config.xml files and config table, looks for the cron schedules and populates the cron schedule table. At this point models are irrelevant. All the cron jobs that have no schedule defined will be skipped. Another method, Cron dispatch, reads the cron jobs from the cron schedule table, collects the methods from both config.xml files and the config table and runs them.

Observer

In the observer will be the method that will be ran by the cron. What code comes here is up to you.

class Inchoo_MyModule_Model_Observer
{
    public function doSomething()
    {
	// do something
    }
}

This is just one example of using cron schedules. There’s plenty of room for modifications and improvements depending on your needs after you get the point how this works.

You made it all the way down here so you must have enjoyed this post! You may also like:

Calling out all Magento developers who want to improve their testing skills – visit MageTestFest! Maja Kardum
Maja Kardum, | 0

Calling out all Magento developers who want to improve their testing skills – visit MageTestFest!

Meet with Inchoo at Magento Imagine 2014! Maja Kardum
Maja Kardum, | 0

Meet with Inchoo at Magento Imagine 2014!

Newsletter auto-subscribe on create account and place order in Magento Marko Martinovic
Marko Martinovic, | 14

Newsletter auto-subscribe on create account and place order in Magento

25 comments

  1. Cron is not running and after checking the exception log i saw this error message , – Undefined property ‘frequency’

    Can anybody help me with this ?

    1. I’m sorry for waiting. It’s a vacation season. There was a bug in the frequency variable. The code is updated.

  2. I think you get error in this line
    (frequency == $frequencyMonthly) ? ‘1’ : ‘*’, # Day of the Month

    What is ‘frequency’ mean?

  3. Hi,
    Cron job usually presupposes applying scripts and special technical knowledge. If someone may be interested in more ready-made solution than usage of coding, there is the software called Magento Store Manager and its addon Automated Product Import. Using this pair you can set up automated import each time period you decide on your own. In case you are interested, you can find details in this article. <a href="
    https://www.mag-manager.com/useful-articles/how-to/how-to-perform-automated-product-import/

  4. This is not working, when I run cron command, cron_schedule table is not populating with my job code record, how it will run please guide?
    Thanks in advance

  5. Could you write all the directory structure you used? I’m a beginner in Magento and it’s always difficult to me to find out what paths I should use. Or, best, could you place your tutorials on github or bitbucket? Then it would be easiest to recognize what should be where. Thanks for tutorial!

  6. i just want to run my method at every 2 hour
    so is it necessary to add system.xml and cron.php file to my module?

  7. i just run my method every five second.
    so is it necessary to generate system.xml and cron.php file?

    1. @Damir,

      I can see missing CRON_MODEL_PATH in your code?

      Just a question, will this code work with or without CRON_MODEL_PATH ?

      I had just discovered that cron is never getting scheduled even if the values are there in database.

  8. I couldn’t get this to work. The schedule table was never populated with my task.
    After some debugging I found out that I have to set config->crontab->jobs->my_cron->schedule->config_path to the same value as CRON_STRING_PATH.
    Otherwise Magento was not able to find the cron_expr.

  9. Will this also work on a per store basis? Where I need a different start time for different stores?

  10. +Ravichandran: You’re going to want to extend or make your own version of adminhtml/system_config_source_cron_frequency, and give it another value similar to “Every X Days”.

    Then you’ll need to create another field in system.xml which allows you to input a number, and set that field’s tag to the frequency field above.

    Finally, you’ll want to modify Cron.php mentioned in this article to also take that value into account. You’ll want to have your new field change the “# Day of the Month” setting in Cron.php to ‘*/X’ where X is the value of the new field. (eg: 7)

  11. I need to set up a cronjob to run for 30 minute every day?
    How can i do ?
    It’s not correct for my setup

    1. //this will run every saturday midnight

      0 0 * * 6

      * * * * * command to be executed
      – – – – –
      | | | | |
      | | | | —– Day of week (0 – 7) (Sunday=0 or 7)
      | | | ——- Month (1 – 12)
      | | ——— Day of month (1 – 31)
      | ———– Hour (0 – 23)
      ————- Minute (0 – 59)

  12. Hello Damir

    Nice post, but it looks like you’re limited to “my_cron” job only. I did something similar a while ago.

    Be sure to delete the cron item from the db when leaving your input empty(!)

    class Mct_CustomModule_Model_Config_Cron extends Mage_Core_Model_Config_Data
    {
    protected function _afterSave()
    {
    if (isset($this->getFieldConfig()->job))
    {
    $job = $this->getFieldConfig()->job;

    $cronStringPath = ‘crontab/jobs/’.$job.’/schedule/cron_expr’;
    $cronModelPath = ‘crontab/jobs/’.$job.’/run/model’;

    $value = $this->getValue();
    $node = (string) Mage::getConfig()->getNode($cronModelPath);

    $schedule = Mage::getModel(‘core/config_data’)
    ->load($cronStringPath, ‘path’);

    $model = Mage::getModel(‘core/config_data’)
    ->load($cronModelPath, ‘path’);

    if($value)
    {
    $schedule->setValue($value)->setPath($cronStringPath)->save();
    $model->setValue($node)->setPath($cronModelPath)->save();
    }
    else
    {
    $schedule->delete();
    $model->delete();
    $this->delete();
    }
    }
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.