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.
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:
- Crear datos de prueba con los que trabajar durante el desarrollo de la aplicación
- 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.
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
]);
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".
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.
Una vez configurado el DatabaseSeeder, podemos lanzar toda la secuencia de seeders a través del comando de artisan:
php artisan db:seed
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();
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:
- name
- slug
- continent
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
}
}
}
- En este código comenzamos creando una categoría raíz. Esta categoría debe tener unos datos particulares que no quiero que se generen aleatoriamente. La categoría raíz tiene la particularidad de tener el "
parent_id
" igual anull
. - Luego creamos una colección de categorías en
$categories
. Esta colección nos servirá para ir insertando en ella todas las categorías generadas, de modo que podamos obtener una categoría aleatoria en la cual colocaremos como hijo las categorías generadas. - El resto de las categorías adicionales las puedo generar con un simple bucle for.
- Dentro del seeder es bastante común utilizar la librería "
faker
", que nos permite generar datos aleatorios y falsos en las columnas de la tabla. Para acceder a faker utilizo el helperfake()
. - En este código estoy utilizando una clase mía propia llamada
CategorySlugGenerator
. El código de esa clase no tiene mucho que ver con este ejemplo, pero simplemente permite generar slugs y asegurarse de que son únicos en la tabla. - Luego creamos la categoría, con los datos necesarios. Dentro de los datos generados destaca el '
parent_id
', que simplemente se genera a través de la selección de una categoría aleatoria de la colección y sacando su atributo "id
". - Por último añadimos cada categoría a la colección de categorías, de modo que cada vez que se cree una nueva categoría pueda ser elegida como categoría padre.
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...