Symfony Commands for Loading and Purging Fixtures

Doctrine already has a command to load fixtures, symfony console doctrine:fixtures:load, but does not take into account the reset of sequences and there is no command just to purge fixtures.

The article describes Symfony commands:

  • Purge Database, Restart Sequences (PostgreSQL) and Load Fixtures
  • Purge Database Without Loading Fixtures

Purge Database, Restart Sequences (PostgreSQL) and Load Fixtures

// src/Command/LoadFixturesCommand.php
<?php

namespace App\Command;

use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
    name: 'app:fixtures:load',
    description: 'Purge database, restart sequences (PostgreSQL) and load fixtures',
)]
class LoadFixturesCommand extends Command
{
    public function __construct(
        private readonly EntityManagerInterface $em
    ) {
        parent::__construct();
    }

    /**
     * @throws ExceptionInterface
     * @throws Exception
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        $io->title('Purge database, restart sequences (PostgreSQL) and load fixtures');

        if ($input->isInteractive()) {
            if (
                !$io->confirm(
                    "Careful, database will be purged and sequences (PostgreSQL) restarted.\n Do you want to continue?",
                    false
                )
            ) {
                return Command::SUCCESS;
            }
        }

        $io->section('Restarting sequences (PostgreSQL)');

        /** @var array $sequences */
        $sequences = $this->em
            ->getConnection()
            ->executeQuery("SELECT c.relname FROM pg_class c WHERE (c.relkind = 'S')")
            ->fetchFirstColumn()
        ;

        foreach ($sequences as $sequence) {
            $this->em->getConnection()
                ->executeQuery('ALTER SEQUENCE ' . $sequence . ' RESTART WITH 1');

            $io->writeln($sequence);
        }

        $io->section('Purging database and loading fixtures');

        $application = $this->getApplication();
        if (!$application) {
            return Command::FAILURE;
        }
        
        $command = $application->find('doctrine:fixtures:load');

        $arrayInput = new ArrayInput([]);
        $arrayInput->setInteractive(false);

        $returnCode = $command->run($arrayInput, $output);
        if (Command::SUCCESS !== $returnCode) {
            return $returnCode;
        }

        $io->newLine(2);
        $io->success('Fixtures loaded');

        return Command::SUCCESS;
    }
}

Run command:

symfony console app:fixtures:load
Purge database, restart sequences (PostgreSQL) and load fixtures
================================================================

 Careful, database will be purged and sequences (PostgreSQL) restarted.
 Do you want to continue? (yes/no) [no]:
 > yes

Restarting sequences (PostgreSQL)
---------------------------------

brand_id_seq
category_id_seq
photo_id_seq
product_id_seq
stock_id_seq

Purging database and loading fixtures
-------------------------------------


   > purging database
   > loading App\DataFixtures\BrandFixture
   > loading App\DataFixtures\CategoryFixture
   > loading App\DataFixtures\StockFixture
   > loading App\DataFixtures\ProductFixture
   > loading App\DataFixtures\PhotoFixture


                                                                                
 [OK] Fixtures loaded                                                           
                                                                                


Purge Database Without Loading Fixtures

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'
            
    app.orm_purger:
        class: 'Doctrine\Common\DataFixtures\Purger\ORMPurger'
        
    Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface: '@app.orm_purger'
// src/Command/PurgeFixturesCommand.php
<?php

namespace App\Command;

use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
    name: 'app:fixtures:purge',
    description: 'Purge fixtures',
)]
class PurgeFixturesCommand extends Command
{
    public function __construct(
        private readonly ORMPurgerInterface $purger
    ) {
        parent::__construct();
    }
    
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        $io->title('Purge fixtures');

        if ($input->isInteractive()) {
            if (!$io->confirm(
                "Careful, database will be purged.\n Do you want to continue?",
                false
            )) {
                return Command::SUCCESS;
            }
        }

        $this->purger->purge();

        $io->success('Fixtures purged');
        
        return Command::SUCCESS;
    }
}

Run command:

symfony console app:fixtures:purge
Purge fixtures
==============

 Careful, database will be purged.
 Do you want to continue? (yes/no) [no]:
 > yes

                                                                                
 [OK] Fixtures purged