Cron-Jobs
Über das Cron-System des JTL-Shop können wiederkehrende Aufgaben ausgeführt werden. Neben den vordefinierten ist es auch möglich, dem System neue Jobs per Plugin hinzuzufügen.
Cron-Job im System auswählbar machen
Um einen selbsdefinierten Cron-Job über die Cron-Verwaltung im Backend auswählbar zu machen, muss er zunächst in die Liste
der verfügbaren Cron-Jobs eingefügt werden. Dies passiert über das Event GET_AVAILABLE_CRONJOBS
(im Namespace \JTL\Events\Event
) des EventDispatchers.
Im Bootstrapper des Plugins wird dazu ein Listener für dieses Event registriert
und der eigene Cron-Job-Type in die Liste der verfügbaren Job-Typen eingefügt.
public const CRON_TYPE = 'plugin:cronjob_plugin.testcron';
public function boot(Dispatcher $dispatcher): void
{
parent::boot($dispatcher);
//...
$dispatcher->listen(\JTL\Events\Event::GET_AVAILABLE_CRONJOBS, [$this, 'availableCronjobType']);
//...
}
public function availableCronjobType(array &$args): void
{
if (!\in_array(self::CRON_TYPE, $args['jobs'], true)) {
$args['jobs'][] = self::CRON_TYPE;
}
}
Damit das Cron-System anhand des registrierten Cron-Types den richtigen Cron-Job aufrufen kann, ist noch ein Mapping auf
eine Job-Klasse notwendig. Diese Klasse muss das Interface \JTL\Cron\JobInterface
implementieren. Das Mapping
passiert über das Event MAP_CRONJOB_TYPE
des EventDispatchers. Im Bootstrapper
des Plugins wird dazu ein weiterer Listener hinzugefügt und dort der eigene Cron-Job-Type auf eine Job-Klasse gemapped.
public const CRON_TYPE = 'plugin:cronjob_plugin.testcron';
public function boot(Dispatcher $dispatcher): void
{
parent::boot($dispatcher);
//...
$dispatcher->listen(\JTL\Events\Event::GET_AVAILABLE_CRONJOBS, [$this, 'availableCronjobType']);
$dispatcher->listen(\JTL\Events\Event::MAP_CRONJOB_TYPE, [$cronHelper, 'mappingCronjobType']);
//...
}
public function availableCronjobType(array &$args): void
{
if (!\in_array(self::CRON_TYPE, $args['jobs'], true)) {
$args['jobs'][] = self::CRON_TYPE;
}
}
public function mappingCronjobType(array &$args): void
{
/** @var string $type */
$type = $args['type'];
if ($type === self::CRON_TYPE) {
$args['mapping'] = MyCustomCronJob::class;
}
}
Eigene Job-Klasse implementieren
Nach der Registrierung und dem Mapping kann der eigene Cron-Job über die Cron-Verwaltung ausgewählt, aktiviert und
auch wieder gelöscht werden. Die konkrete Funktionalität wird in einer eigenen Job-Klasse implementiert.
Diese Klasse muss das Interface JTL\Cron\JobInterface
implementieren und kann dazu einfach die abstrakte
Klasse JTL\Cron\Job
erweitern. Es muss mindestens die Methode start(QueueEntry $queueEntry): JobInterface
implementiert werden.
use JTL\Cron\Job;
use JTL\Cron\JobInterface;
use JTL\Cron\QueueEntry;
/**
* Class CronJob
*/
class MyCustomCronJob extends Job
{
public function start(QueueEntry $queueEntry): JobInterface
{
parent::start($queueEntry);
$this->garbageCollect();
$this->setFinished($this->doSomething());
return $this;
}
private function doSomething(): bool
{
//... do whatever to do if cron job is called
return true;
}
}
Wann ist ein Cron-Job "fertig"?
Der Sinn von Cron-Jobs besteht - neben dem zeitgesteuerten Ausführen von Aufgaben - auch darin, größere Abläufe in kleinere
"Häppchen" zu packen und diese in mehreren Durchläufen zu erledigen.
Dazu wird ein Job, nachdem er zu einem bestimmten Zeitpunkt "gestartet" wurde, solange zyklisch aufgerufen, bis er über setFinished(true)
signalisiert, dass er "fertig" ist. Um z.B. eine Liste an Einträgen in 10er-Schritten abzuarbeiten, könnte die Methode
doSomething(): bool
so implementiert werden:
private function doSomething(): bool
{
$listLimit = 10;
$listCount = $this->getListCount();
$listItems = $this->getListItems($listLimit);
foreach ($listItems as $listItem) {
$this->finishListItem($listItem);
}
return $listCount <= $listLimit;
}
Eine detailiertere Info zum aktuellen Stand der Abarbeitung kann über die JTL\Cron\QueueEntry
-Instanz $queueEntry
der start
-Methode realisiert werden.
Programmatisches Anlegen eines Cron-Jobs
Wenn der eigene Cron-Job nicht nur durch Nutzer-Interaktion im Backend des JTL-Shop angelegt werden soll, sondern auch
automatisch bei einer bestimmten Plugin-Einstellung, so kann dieser über Methoden des JTL\Router\Controller\Backend\CronController
angelegt und auch wieder gelöscht werden.
public function installCron(int $frequency = 6, string $startTime = '02:00'): void
{
$controller = Shop::Container()->get(JTL\Router\Controller\Backend\CronController::class);
$controller->addQueueEntry([
'type' => self::CRON_TYPE,
'frequency' => $frequency,
'time' => $startTime,
'date' => (new DateTime())->format('Y-m-d H:i:s'),
]);
}
public function uninstallCron(): void
{
$controller = Shop::Container()->get(JTL\Router\Controller\Backend\CronController::class);
$cron = \array_filter($controller->getJobs(), static function (JobInterface $job) {
return $job->getType() === self::CRON_TYPE;
});
if (\count($cron) !== 0) {
$controller->deleteQueueEntry(\array_shift($cron)->getCronID());
}
}
Automatisches Anlegen bei Installation des Plugins
Soll ein Plugin-Cron-Job bei der Installation eines Plugins automatisch eingerichtet werden kann dafür die Methode
installed
des Bootstrappers genutzt werden.
public function installed(): void
{
parent::installed();
$cronJob = new PluginCronJob();
$cronJob->installCron(3, '01:30');
}
Bei der Deinstallation des Plugins muss der Cron-Job dann über die Methode uninstalled
wieder entfernt werden.
public function uninstalled(bool $deleteData = true): void
{
parent::uninstalled($deleteData);
$cronJob = new PluginCronJob();
$cronJob->uninstallCron(3, '01:30');
}
Hint
Die komplette Implementation eines Plugin-Cron-Job ist im JTL-Demo Plugin realisisert. Das Plugin ist über Gitlab frei verfügbar.