Skip to main content

Adding a Cronjob

This guide walks through creating a new scheduled command in Logidav, from writing the command class to registering it in the system crontab.

1. Create the Command Class

Place your command in the appropriate subdirectory of src/AppBundle/Command/. Commands are organized by domain: Sale/, Product/, Stock/, Payment/, Refund/, etc.

<?php
// src/AppBundle/Command/Product/SyncProductCatalogCommand.php

namespace AppBundle\Command\Product;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\LockableTrait;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class SyncProductCatalogCommand extends ContainerAwareCommand
{
use LockableTrait;

protected function configure()
{
$this
->setName('app:product:sync-catalog')
->setDescription('Synchronize product catalog from external source');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$this->lock()) {
$output->writeln('The command is already running in another process.');
return 0;
}

try {
$container = $this->getContainer();
$logger = $container->get('logger');

$logger->info('Starting product catalog sync');

// Your business logic here
$service = $container->get('app.service.product');
$result = $service->syncCatalog();

$logger->info('Product catalog sync completed', [
'processed' => $result['processed'],
'errors' => $result['errors'],
]);

$output->writeln(sprintf('Sync complete: %d processed, %d errors',
$result['processed'],
$result['errors']
));
} catch (\Exception $e) {
$logger->error('Product catalog sync failed', [
'error' => $e->getMessage(),
]);
throw $e;
} finally {
$this->release();
}

return 0;
}
}
warning

Always use LockableTrait on commands that run on cron. Without it, overlapping executions can cause duplicate processing, data corruption, or resource exhaustion. The lock is file-based and stored in the system temp directory.

2. Register as a Service

Add your command to src/AppBundle/Resources/config/services.yml:

services:
app.command.product.sync_catalog:
class: AppBundle\Command\Product\SyncProductCatalogCommand
tags:
- { name: console.command }

3. Add to System Crontab

The source of truth for all scheduled commands is docs/system-crontab.md. Edit this file to add your new entry.

All cron entries use the run_job.sh wrapper, which provides JSON-structured logging and error capture:

# Product catalog sync - every 10 minutes
*/10 * * * * /path/to/run_job.sh php bin/console app:product:sync-catalog -e=prod

Choose an appropriate schedule based on the command's requirements:

FrequencyCron ExpressionUse Case
Every minute* * * * *Real-time sync, urgent queues
Every 5 minutes*/5 * * * *Stock updates, order polling
Every 10 minutes*/10 * * * *Catalog sync, report refresh
Hourly0 * * * *Aggregations, cleanup
Daily0 2 * * *Full reindex, heavy reports

4. Generate Documentation

After adding the crontab entry, regenerate the cronjob documentation:

python3 tools/cronjob_doc_generator.py

Verify your new command appears in the generated output. This documentation is used by the operations team to understand what runs and when.

5. Test Locally

Run the command in the dev environment:

php bin/console app:product:sync-catalog -e=dev

Check for output in the terminal and in the log file:

tail -f var/logs/dev.log | grep "product catalog"
tip

Use the -v, -vv, or -vvv flags for increasing verbosity during development. This is especially helpful for debugging data transformation issues.

Checklist

  • Command class created with LockableTrait
  • Service registered with console.command tag
  • Crontab entry added to docs/system-crontab.md
  • Documentation regenerated with cronjob_doc_generator.py
  • Tested locally in dev environment
  • Logs verified for correct output

See Also