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.

Note: These are correct, real Symfony CLI commands meant to be run inside your own project's terminal — this website doesn't have Symfony installed, so nothing here executes live on this page.

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.

MethodResponsibility
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.

Next up: Head to Security to control who is allowed to trigger sensitive actions like publishing an article in the first place.