سؤال

In Magento 1, instead of declaring the cron expression directly like this:

<crontab> 
    <jobs> 
        <vendor_module_cron>
            <schedule>*/30 * * * *</schedule>
            <run>
                <model>module/observer::runCron</model>
            </run>
        </vendor_module_cron>
    </jobs> 
</crontab>

you could setup your crontab like this in the config.xml:

<crontab> 
    <jobs> 
        <vendor_module_cron>
            <schedule>
                <config_path>vendor/module/cron_expr</config_path>
            </schedule>
            <run>
                <model>module/observer::runCron</model>
            </run>
        </vendor_module_cron>
    </jobs> 
</crontab>

Where Magento would use Mage::getStoreConfig((string)$jobConfig->schedule->config_path) to get the cron schedule expression.

This way you could dynamically change the schedule from the backend without having to modify the config.xml file directly.

How can we achieve this in Magento 2 ? Is it still possible ?

هل كانت مفيدة؟

المحلول

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

It's kinda simple.
I got this from module Mageplaza\ImageOptimizer:
You only need to load config_path in $this->_configValueFactory, and magento will automatic run the cron for you.

app/code/Mageplaza/ImageOptimizer/etc/adminhtml/system.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="mpimageoptimizer" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0">
            <group id="cron_job" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0">
                <label>Cron Job</label>
                <field id="scan_schedule" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
                    <label>Scan Schedule</label>
                    <backend_model>Mageplaza\ImageOptimizer\Model\Config\Backend\Cron\Schedule</backend_model>
                </field>
            </group>
        </section>
    </system>
</config>

app/code/Mageplaza/ImageOptimizer/Model/Config/Backend/Cron/Schedule.php:

<?php

namespace Mageplaza\ImageOptimizer\Model\Config\Backend\Cron;

use Exception;
use Magento\Framework\App\Cache\TypeListInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Value;
use Magento\Framework\App\Config\ValueFactory;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Message\ManagerInterface;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;

/**
 * Class Schedule
 * @package Mageplaza\ImageOptimizer\Model\Config\Backend\Cron
 */
class Schedule extends Value
{
    /**
     * Cron scan path
     */
    const CRON_SCAN_PATH = 'crontab/default/jobs/mpimageoptimizer_cronjob_scan/schedule/cron_expr';

    /**
     * @var ValueFactory
     */
    protected $_configValueFactory;

    /**
     * @var ManagerInterface
     */
    protected $messageManager;

    /**
     * Schedule constructor.
     *
     * @param Context $context
     * @param Registry $registry
     * @param ScopeConfigInterface $config
     * @param TypeListInterface $cacheTypeList
     * @param ValueFactory $configValueFactory
     * @param ManagerInterface $messageManager
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        ScopeConfigInterface $config,
        TypeListInterface $cacheTypeList,
        ValueFactory $configValueFactory,
        ManagerInterface $messageManager,
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null,
        $runModelPath = '',
        array $data = []
    ) {
        $this->_configValueFactory = $configValueFactory;
        $this->messageManager      = $messageManager;

        parent::__construct(
            $context,
            $registry,
            $config,
            $cacheTypeList,
            $resource,
            $resourceCollection,
            $data
        );
    }

    /**
     * @return Value
     */
    public function afterSave()
    {
        $enableScan       = $this->getData('groups/cron_job/fields/enabled_scan/value');
        $scanSchedule     = $this->getData('groups/cron_job/fields/scan_schedule/value');

        if ($enableScan) {
            try {
                $this->_configValueFactory->create()->load(
                    self::CRON_SCAN_PATH,
                    'path'
                )->setValue(
                    $scanSchedule
                )->setPath(
                    self::CRON_SCAN_PATH
                )->save();
            } catch (Exception $e) {
                $this->messageManager->addErrorMessage(__('We can\'t save the cron expression. %1', $e->getMessage()));
            }
        }

        return parent::afterSave();
    }
}

app/code/Mageplaza/ImageOptimizer/etc/crontab.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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="mpimageoptimizer_cronjob_scan" instance="Mageplaza\ImageOptimizer\Cron\Scan" method="execute">
            <config_path>crontab/default/jobs/mpimageoptimizer_cronjob_scan/schedule/cron_expr</config_path>
        </job>
    </group>
</config>

app/code/Mageplaza/ImageOptimizer/Cron/Scan.php:

<?php

namespace Mageplaza\ImageOptimizer\Cron;

use Exception;
use Psr\Log\LoggerInterface;

/**
 * Class Scan
 * @package Mageplaza\ImageOptimizer\Cron
 */
class Scan
{
    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Scan constructor.
     *
     * @param LoggerInterface $logger
     */
    public function __construct(
        LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    /**
     * @return $this
     */
    public function execute()
    {
        try {
            // do something
        } catch (Exception  $e) {
            $this->logger->critical($e->getMessage());
        }

        return $this;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى magento.stackexchange
scroll top