Uso de Fixtures en Symfony 5

Los fixtures son clases PHP en donde creamos objetos y los persistimos en la base de datos. Estas clases deben extender de Doctrine\Bundle\FixturesBundle\Fixture y deben implementar el método load().

Requiere la instalación del Bundle doctrine-fixtures-bundle

> composer require --dev doctrine/doctrine-fixtures-bundle

El siguiente ejemplo muestra un fixtures que crea 20 productos y los persiste:

// src/DataFixtures/AppFixtures.php

namespace App\DataFixtures;

use App\Entity\Producto;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;

class AppFixtures extends Fixture
{
		public function load(ObjectManager $manager)
		{
			// Creamos y persistimos 20 productos
			for ($i = 0; $i < 20; $i++) {
				$product = new Producto();
				$product->setNombre('product '.$i);

				$product->setPrecio(mt_rand(10, 100));
				$manager->persist($product);
			}
	
			$manager->flush();
		}
}

El comando para cargar la base de datos con fixtures es:

> bin/console doctrine:fixtures:load

Este comando, primero purga la base de datos. Si queremos añadir los fixtures sin borrar previamente la base de datos, podemos añadir la opción –append al comando.

> bin/console doctrine:fixtures:load --append

Dividir los fixtures en ficheros separados

En ocasiones, la clase de fixtures crece demasiado y decidimos separarla en 2 o más clases. Symfony soluciona los dos asuntos más comunes que se dan en estos casos: compartir objetos entre las clases fixture y cargar los fixtures en orden.

Compartir objetos entre Fixtures

Symfony nos proporciona dos métodos addReference() y getReference() para compartir objetos entre fixtures.

// src/DataFixtures/UserFixtures.php

class UserFixtures extends Fixture
{
		public const ADMIN_USER_REFERENCE = 'admin-user';

		public function load(ObjectManager $manager)
		{
			$userAdmin = new User('admin', 'pass_1234');
			$manager->persist($userAdmin);
			$manager->flush();

			// other fixtures can get this object using the 							UserFixtures::ADMIN_USER_REFERENCE constant

			$this->addReference(self::ADMIN_USER_REFERENCE, $userAdmin);
		}
}

// src/DataFixtures/GroupFixtures.php

class GroupFixtures extends Fixture
{
		public function load(ObjectManager $manager)
		{
			$userGroup = new Group('administrators');

			// this reference returns the User object created in UserFixtures
			
			$userGroup->addUser(
			$this->getReference(UserFixtures::ADMIN_USER_REFERENCE));

			$manager->persist($userGroup);
			$manager->flush();
		}
}

Lo único que queda por solucionar es que estas clases deben ejecutarse en un orden determinado.

Cargar los ficheros de fixtures en orden

Una clase fixture que dependa de otra, debe implementar el interfaz DependentFixtureInterface y añadir un método getDependencies() en el que se indican las clases de las que depende.

// src/DataFixtures/UserFixtures.php

namespace App\DataFixtures;

class UserFixtures extends Fixture
{
		public function load(ObjectManager $manager)
		{
			// ...
		}
}


// src/DataFixtures/GroupFixtures.php

namespace App\DataFixtures;

use App\DataFixtures\UserFixtures;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;

class GroupFixtures extends Fixture implements DependentFixtureInterface
{
		public function load(ObjectManager $manager)
		{
			// ...
		}

		public function getDependencies()
		{
			return array(
				UserFixtures::class,
			);
		}
}

Symfony ejecutará las clases en un orden que respete las dependencias.

Nota: para crear fixtures más complejos (http://github.com/hautelook/AliceBundle)