Console Commands
Every Symfony project ships with bin/console, a single entry point for running
commands from the terminal instead of a browser. It gives you access to a large set of
built-in commands for everyday development tasks, and it's also the tool you
use to run custom commands that you write yourself for jobs that don't belong
behind an HTTP request.
Built-In Commands
A few built-in commands cover most day-to-day needs: scaffolding new code, clearing the cache after configuration changes, and inspecting how the application is wired up.
bin/console make:controller ArticleController
bin/console cache:clear
bin/console debug:router
debug:router is especially useful when something isn't routing the way you expect —
it prints every registered route, its name, its URL pattern, and the controller it maps to, all
in one table, which makes tracking down a routing mistake far faster than reading configuration
files by hand.
Anatomy of a Custom Command
A custom command is a class that extends Symfony's Command base class and
implements two methods: configure(), where you set the command's name,
description, and any arguments it accepts, and execute(), where the actual logic
runs.
| Method | Responsibility |
|---|---|
configure() | Declares the command's name (e.g. app:send-newsletter), description, and arguments/options |
execute() | Contains the actual work the command performs, and returns a status code |
Worked Example: A Newsletter Command
This command loops over every published article and reuses the ArticleNotifier
service built on the previous page — the same class a controller injects, injected here into a
command instead. No duplicated notification logic, no new mailer wiring; just a different way
of triggering the same service.
<?php
namespace App\Command;
use App\Repository\ArticleRepository;
use App\Service\ArticleNotifier;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'app:send-newsletter')]
class SendNewsletterCommand extends Command
{
public function __construct(
private ArticleRepository $articles,
private ArticleNotifier $notifier,
) {
parent::__construct();
}
protected function configure(): void
{
$this->setDescription('Sends a notification email for every published article');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
foreach ($this->articles->findPublished() as $article) {
$this->notifier->notifyPublished($article);
$output->writeln("Notified subscribers about: {$article->getTitle()}");
}
return Command::SUCCESS;
}
}
Notice that ArticleRepository and ArticleNotifier are both received
through the constructor exactly the way a controller would receive them — commands are services
too, so the same autowiring and constructor injection from the previous page applies here
without any extra setup.
Running the Command
Once the command class exists anywhere under src/Command, Symfony picks it up
automatically and it becomes available under its configured name.
bin/console app:send-newsletter
Because this runs from the terminal rather than a web request, it's a natural fit for a cron job or a scheduled task — something that needs to happen periodically with nobody sitting in a browser waiting for a response.