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;
}
}
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:
| Frequency | Cron Expression | Use Case |
|---|---|---|
| Every minute | * * * * * | Real-time sync, urgent queues |
| Every 5 minutes | */5 * * * * | Stock updates, order polling |
| Every 10 minutes | */10 * * * * | Catalog sync, report refresh |
| Hourly | 0 * * * * | Aggregations, cleanup |
| Daily | 0 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"
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.commandtag - 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
- Architecture: Bundle Responsibilities for command design patterns
- Adding a Queue Processor if your cronjob processes queued tasks
- CI/CD Pipeline for how tests run on your new command