> Manuales > Manual de Laravel

Explicaciones detalladas sobre los seeders en Laravel, que te permiten definir código con el que poblar tus tablas. Ejemplos de seeders y la configuración en una aplicación, así como los procedimientos disponibles para ejecutarlos.

Seeders en Laravel

Seguimos con asuntos relacionados con las bases de datos en el Manual de Laravel 5. En esta ocasión vamos a hablar de los "seeders". Seed es "semilla" en inglés y "seeders" serían algo así como "sembradores", aunque prefiero traducir como "alimentador", ya que sería como yo llamaría a estos sistemas corrientemente.

Los seeders no son más que componentes del framework Laravel que sirven para inicializar las tablas con datos. Así como las migraciones nos permiten especificar el esquema de la base de datos, los seeders nos permiten también por medio de código alimentar las tablas con datos.

Su uso, como decimos puede ser para:

  1. Crear datos de prueba con los que trabajar durante el desarrollo de la aplicación
  2. Configurar el estado de las tablas que necesita nuestra aplicación para comenzar a trabajar

Para aclarar este segundo caso piensa en el ejemplo de los artículos. Cuando subes un artículo puede tener varios estados: "borrador", "publicado", "borrado", etc. Puede que esos estados estén definidos en una tabla, así luego puedes crear otros estados si lo necesitas, como "obsoleto". Pero quizás no vas a hacer en el backoffice para administrar los cambios sobre esa tabla y prefieres dejar una configuración inicial de los estados que prevees vas a necesitar para que la aplicación funcione según los requisitos pedidos. Entonces usarás el seeder para generar los estados de los artículos iniciales que te han pedido.

Para abordar los Seeders en Laravel vamos a tratar estos puntos.

Crear un seeder

El proceso para crear un seeder comienza, como tantos otros, con el asistente artisan. Podemos pedirle que nos genere el esqueleto de un alimentador mediante el comando make:seeder indicando luego el nombre del seeder que queremos crear:

php artisan make:seeder ReviewsSeeder

Los seeder pueden tener cualquier nombre que necesites. La recomendación es indicar un nombre que te sirva para saber exactamente para qué se creó ese seeder. Por ejemplo podrás usar el nombre de la tabla que se va a alimentar y el sufijo "Seeder". Además como los seeders en código son clases, colocaremos la primera letra en mayúscula por convención. Algo como "BookSeeder" podría ser buen nombre o "BookTableSeeder".

php artisan make:seeder BookSeeder

Los seeders una vez creados se colocan en la carpeta "database/seeds". Ejecuta el comando anterior y allí encontrarás el seeder creado por artisan.

Escribir el código de los seeder

Los seeder no son más que clases, de programación orientada a objetos, en las que tendrás el código de los datos que quieras insertar para alimentar tus tablas inicialmente. Estas clases tendrán un método llamado run() que contiene el código de los inserts que desees realizar dentro de tus tablas.

Existen varias maneras de "atacar" a la base de datos y producir los inserts que necesitas. Puedes usar sentencias construidas con "Query Builder" o directamente "Eloquent" a través de las operaciones disponibles en un modelo, eso es indiferente.

Nota: Esta parte la podemos describir de manera muy esquemática, porque todavía no hemos profundizado en el conocimiento que necesitamos para trabajar con la base de datos. Por ello nos vamos a limitar a insertar los datos valiéndonos de las operaciones del modelo. Para entender lo que viene a continuación tendrás que haber leído el capítulo de introducción a los modelos en Laravel. Si hace tiempo que lo leíste y no has practicado lo suficiente te recomendamos hacer una segunda lectura antes de proseguir.

Utilizando las operaciones de un modelo, y el ORM Eloquent, podemos insertar datos a través del método create() del modelo. Por ejemplo, si tenemos el modelo "Review", que afecta a la tabla "reviews". Entonces podrás hacer Review::create() para acceder a la operación de inserción de datos.

Este método create() recibe un array asociativo, que contiene cada uno de los campos que se quieren aplicar con los valores a definir. Son pares claves / valor, la clave es el nombre del campo y el valor es el valor que pretendemos asignar.

Review::create([
     'campo' 			=>	'Valor campo',
     'campo_num'		=>	0
]);

Nota: Recuerda que los modelos son clases, que se llaman con el mismo nombre que la tabla (la diferencia es que la primera letra de la tabla es en minúsculas y la primera legra de la clase de un modelo es en mayúsculas, así como la tabla está en plural y el modelo en singular). Esas clases en principio pueden estar vacías de código, puesto que la herencia de la clase principal Model ya nos ofrece la mayoría de las operaciones listas para usar. Para crear un modelo simplemente lanzas el correspondiente comando de artisan make:model. Será algo que necesitarás realizar antes de poder invocar el método create() del modelo. De nuevo, lee el artículo de los modelos si no te suena lo que estamos diciendo.

Además, en el caso que te apoyes en el modelo para crear los alimentadores, tendrás que asegurarte que haces el correspondiente "use" para tener disponible el espacio de nombres (namespace) de la clase del modelo que vas a usar.

Por ejemplo, este sería el código para nuestro seeder de Reviews. El tema del namespace, para conocer el modelo Review está en la línea "use App\Review;".

<?php

use Illuminate\Database\Seeder;
use App\Review;

class ReviewsSeeder extends Seeder
{
    public function run()
    {
        Review::create([
        	'name' 				=>	'Vacas locas',
        	'votes'				=>	33,
        	'fulldescription'			=>	'lalala'
        ]);
    }
}

Ejecutar los seeders

En un proyecto podemos haber creado varios seeders, para alimentar de manera individual varias tablas. Estos alimentadores se pueden ejecutar de manera global (todos a la vez) configurando el código de una clase que está en la carpeta "database/seeds" llamada "DatabaseSeeder".

Nota: DatabaseSeeder la encontrarás de manera predeterminada en toda instalación de Laravel 5 y con un código básico para comenzar. Esta clase podríamos decir que es el seeder "global". Podríamos situar todo el código de las alimentaciones en este mismo archivo, en el método run(), pero no es lo habitual, puesto que resulta de mayor utilidad separar los seeders en diversos archivos como hemos aprendido.

Dentro de DatabaseSeeder, en el método run() podremos hacer tantas llamadas a seeders particulares a través del método call(), indicando qué seeders queremos ejecutar a través del nombre de la clase.

public function run()
{
    Model::unguard();

     $this->call(BookSeeder::class);
     $this->call(ReviewsSeeder::class);

    Model::reguard();
}

Las distintas clases seeder que hayas especificado se ejecutarán en el orden en el que estén escritas en este código.

Nota: Los métodos de Model unguard() y reguard() sirven para anular temporalmente y luego reactivar ciertas protecciones de seguridad en el modelo, que evitan que te inyecten datos indeseados en las tablas. Hablaremos más adelante de ellas.

Una vez configurado el DatabaseSeeder, podemos lanzar toda la secuencia de seeders a través del comando de artisan:

php artisan db:seed

Nota: Para realizar el "migrate" y a la vez procesar los seeders, en un solo paso, tienes como alternativa de lanzar el siguiente comando.

php artisan migrate:refresh --seed

Este comando lo podemos ejecutar un número indeterminado de veces. Si se repite su ejecución simplemente volverá a insertar los datos de nuevo en las tablas.

Ejecutar seeders de manera individual

En ocasiones podemos necesitar realizar la ejecución de un seeder en concreto y no todos los generados para un proyecto. Para conseguir esto podemos usar el comando de artisan "bd:seed" seguido del parámetro --class, al que le asignamos como valor el nombre del seeder a ejecutar de manera individual.

Quedaría algo como puedes ver en siguiente código:

php artisan db:seed --class=BookSeeder

Quizás nos ocurra entonces que recibamos un error: [Illuminate\Database\Eloquent\MassAssignmentException]

Eso es por una protección que tiene Laravel para evitar que introduzcan datos que no deseamos en tablas. Lo veremos con detalle más adelante pero se soluciona colocando una pequeña declaración en la propiedad $fillable del modelo. No te preocupes de momento por ello, nosotros vamos a saltarnos las protecciones para este paso de la siguiente manera. Vamos a avisar al modelo que vamos a realizar la alimentación saltándonos la protección con el método unguard() de la clase Model.

Model::unguard();

Nota: La llamada a unguard() es una operación que encontrarás en el código del "seeder global" DatabaseSeeder. Verás ahí el código para levantar las protecciones de nuevo.

Claro, si vas a usar la clase Model debes indicarlo con el correspondiente "use" para conocer su namespace.

use Illuminate\Database\Eloquent\Model;
Con estas dos líneas, el código de un seeder para que puedas ejecutarlo de manera individual te quedaría parecido a esto:

<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\Book;

class BookSeeder extends Seeder
{
    public function run()
    {
        Model::unguard();

        Book::create([
        	'name' 		=>	'Viaje al centro de la tierra',
        	'author'	=>	'Julio Verne',
        	'isbn'		=>	'14445884'
        ]);
    }
}

Ejemplo de un seeder Laravel para una tabla de países

Ahora vamos a ver un ejemplo de seeder que podríamos usar para poblar una tabla de países (tabla countries). En los países vamos a tener tres campos de datos:

Aunque en muchas ocasiones usamos la librería Faker para conseguir insertar datos "inventados", vamos a ver un seeder que hace uso de la fachada DB para introducir datos reales de estos países, que podemos indicar por medio de un array. Luego veremos un ejemplo también con faker.

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class CountrySeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $countries = [
            ['name' => 'Argentina', 'slug' => 'argentina', 'continent' => 'Sudamérica'],
            ['name' => 'Brasil', 'slug' => 'brasil', 'continent' => 'Norte América'],
            ['name' => 'Canada', 'slug' => 'canada', 'continent' => 'Norte América'],
            ['name' => 'España', 'slug' => 'espana', 'continent' => 'Europa'],
            // todos los países que quieras introducir
        ];

        foreach ($countries as $country) {
            DB::table('countries')->insert([
                'name' => $country['name'],
                'slug' => $country['slug'],
                'continent' => $country['continent'],
            ]);
        }
    }
}

Como puedes ver, tienes mucha libertad a la hora de programar tus seeders, usando las herramientas que Laravel te proporciona para insertar los datos en la base de datos.

Ejemplo de un Seeder de Laravel para un árbol de categorías

Dentro de las distintas maneras que tienes en Laravel para poder crear contenido con el cual inicializar tu base de datos, los seeders son bastante potentes porque nos permiten una configuración totalmente arbitraria de lo que insertamos en las tablas.

Para demostrar un caso de uso de los Seeders de Laravel vamos a poner un ejemplo un poquito más complejo de los que hemos visto anteriormente en este artículo. Nuestro objetivo en este ejercicio consiste en crear un seeder de un árbol de categorías, haciendo que estas categorías estén unas dentro de otras.

Podríamos abordar este problema mediante distintas estrategias, pero gracias a la posibilidad de incluir código arbitrario en los seeders de Laravel vamos a reducir bastante la complejidad del ejercicio.

Veamos el código completo y enseguida explicamos algunos de los puntos más relevantes para poder entenderlo.

<?php

namespace Database\Seeders;

use App\Models\Category;
use Illuminate\Database\Seeder;
use App\Lib\Categorys\CategorySlugGenerator;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;

class CategoryTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        // Crea la categoría raíz
        $rootCategory = Category::create([
            'name' => 'Categorías',
            'slug' => 'categorias',
            'parent_id' => null,
            'order' => 0,
        ]);

        $categories = collect([$rootCategory]);

        // Crea categorías adicionales
        for ($i = 1; $i < 1000; $i++) {
    
            $name = fake()->word;
            $slugCreator = new CategorySlugGenerator($name);
            $slug = $slugCreator->createSlug();

            $category = Category::create([
                'name' => $name,
                'slug' => $slug,
                'parent_id' => $categories->random()->id,
                'order' => $i, // El orden que sea el índice es suficiente para mi lógica
            ]);

            $categories->push($category); // Agrega la nueva categoría a la colección para futuras iteraciones
        }
    }
}

Eso es todo, esperamos que este ejercicio te haya resultado relevante y que te haya ayudado a entender cómo podrías crear tus propios seeders en Laravel.

Conclusión

De los seeders no hay mucho más que hablar. Pero lógicamente nuestro código se puede complicar mucho en función de diversas situaciones que queramos resolver, como cargar juegos de datos (varios reg en vez de uno), borrar datos que pudiera haber que no se desean en el estado inicial, etc.

Antes de terminar te damos una sugerencia de código rápido para poder alimentar una tabla con una gran cantidad de elementos, simplemente a través de un bucle for.

for ($i=0; $i<100; $i++){
	Book::create([
    	'name' 		=>	'Libro ' . $i,
    	'author'	=>	'Autor ' . $i,
    	'isbn'		=>	'14445884' . $i
    ]);
}

Para mejorar estos resultados podrías aprender a manejar Faker, una librería pensada para generar datos de prueba de modo que parezcan más reales, pero no es algo que de momento no vamos a tocar, pues es una librería externa a Laravel.

Además nos quedaría explorar toda la parte de acceso a datos con los métodos de los modelos, pero eso es algo que ya no depende del sistema de seeder y que veremos en los siguientes artículos.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual