In application development you usually have migrations you run to pre-seed default application schema and data into the database (it is also used for updating and rolling back database changes). There is an issue if you use this approach with Fixtures though because Fixtures will automatically clear every table when they run. This isn't really what you want if you have default application data that shouldn't be cleared.

You've been able to exclude tables from being cleared by passing the --exclude option to doctrine:fixtures:load for a while now (there is also the less handy --append option). There is an issue with this approach though: You have to remember what tables you aren't supposed to clear and specify each one on the fixtures load command manually (every time you run it). I find this really awful for local development and it actually slows me down (even placing the command in the app readme and copy-pasting everytime is slow).

You could also just specify the data twice: once in fixtures and once in migrations. The problem with this is now you have to maintain the same information in two places.

So instead we will create a new CustomPurgerFactory class that will always pass what tables we want excluded to the ORMPurger class.

The Code

The code is actually pretty short and super easy to understand. First we create our CustomPurgerFactory.php (change the app_settings table to whatever table or tables you want ignored).

<?php

namespace App\DataFixtures\Purger;

use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
use Doctrine\ORM\EntityManagerInterface;

class CustomPurgerFactory implements PurgerFactory
{
    public function createForEntityManager(?string $emName, EntityManagerInterface $em, array $excluded = [], bool $purgeWithTruncate = false): PurgerInterface
    {
        $excluded[] = 'app_settings';

        return new ORMPurger($em, $excluded);
    }
}

Now we need to specify our service definition in services.yaml:

    doctrine.fixtures.purger.orm_purger_factory:
        class: App\DataFixtures\Purger\CustomPurgerFactory
        tags:
            - { name: 'doctrine.fixtures.purger_factory', alias: 'default' }

Now run you fixtures and your table will be left alone without the need to specify it as an argument.

Conclusion

Even though there were already solutions to this problem I didn't like any of them and decided to go this route to make it easier to maintain and use over time. I couldn't find anyone else that shared something similar so I decided to post it here. If you found this useful feel free to say so down below in the comments. I do enjoy reading comments from my readers.