Magento 2: how to use a configuration field for the crontab expression?

Following ProductAlerts in M2:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="catalog_product_alert" instance="Magento\ProductAlert\Model\Observer" method="process">
            <config_path>crontab/default/jobs/catalog_product_alert/schedule/cron_expr</config_path>
        </job>
    </group>
</config>

You should be able to define something similarly:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="custom_job_name" instance="Namespace\Module\Model\Observer" method="process">
            <config_path>namespace/module/schedule/cron_expr</config_path>
        </job>
    </group>
</config>

You could leverage the way they interpret the expression through multiple fields but it shouldn't be required, such as:

<group id="productalert_cron" translate="label" type="text" sortOrder="260" showInDefault="1" showInWebsite="0" showInStore="0">
    <label>Product Alerts Run Settings</label>
    <field id="frequency" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
        <label>Frequency</label>
        <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model>
        <backend_model>Magento\Cron\Model\Config\Backend\Product\Alert</backend_model>
    </field>
    <field id="time" translate="label" type="time" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
        <label>Start Time</label>
    </field>

This would make the input user-friendly (so-to-speak) but is not required, a simple text field that allows you to enter an expression like 7 7 * * * would be fine as well, I would just put some validation on that field to make sure the expression is correct.


You can achieve this same as magento 1

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="my_cron" instance="Namespace\Modulename\Observer\Cron" method="execute">
            <config_path>section-id/group-id/field-id</config_path>
        </job>
    </group>
</config>

See code vendor\magento\module-product-alert\etc\crontab.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="catalog_product_alert" instance="Magento\ProductAlert\Model\Observer" method="process">
            <config_path>crontab/default/jobs/catalog_product_alert/schedule/cron_expr</config_path>
        </job>
    </group>
</config>

It is a config path crontab/default/jobs/catalog_product_alert/schedule/cron_expr it has time schedule value if you see in database core_config_data

vendor\magento\module-cron\Model\Config\Backend\Product\Alert.php

in afterSave function

 const CRON_STRING_PATH = 'crontab/default/jobs/catalog_product_alert/schedule/cron_expr';

    public function afterSave()
    {
        $time = $this->getData('groups/productalert_cron/fields/time/value');
        $frequency = $this->getData('groups/productalert_cron/fields/frequency/value');

        $cronExprArray = [
            intval($time[1]), //Minute
            intval($time[0]), //Hour
            $frequency == \Magento\Cron\Model\Config\Source\Frequency::CRON_MONTHLY ? '1' : '*', //Day of the Month
            '*', //Month of the Year
            $frequency == \Magento\Cron\Model\Config\Source\Frequency::CRON_WEEKLY ? '1' : '*', //Day of the Week
        ];

        $cronExprString = join(' ', $cronExprArray);

        try {
            $this->_configValueFactory->create()->load(
                self::CRON_STRING_PATH,
                'path'
            )->setValue(
                $cronExprString
            )->setPath(
                self::CRON_STRING_PATH
            )->save();
            $this->_configValueFactory->create()->load(
                self::CRON_MODEL_PATH,
                'path'
            )->setValue(
                $this->_runModelPath
            )->setPath(
                self::CRON_MODEL_PATH
            )->save();
        } catch (\Exception $e) {
            throw new \Exception(__('We can\'t save the cron expression.'));
        }

        return parent::afterSave();
    }

In above function set that config_path value from store > Configuration > Catalog > catalog > Product Alerts Run Settings