Skip to main content

Database Seeding

Formidable includes the ability to seed your database with data using Knex.js seeders. All seeders are stored in the database/seeders directory.

Writing Seeders

To generate a seeder, execute the make:seed Craftsman command. All seeders generated by the framework will be placed in the database/seeders directory:

node craftsman make:seed UserSeeder --table="users"

A seeder contains one export by default: seed. This export is an async function which is called when the db:seed Craftsman command is executed. Within the function, you may insert data into your database however you wish. You may use the Knex.js query builder to manually insert data or use factories.

Using Factories

Here's an example of a DatabaseSeeder that uses factories to create data:

database/seeders/DatabaseSeeder.ts
import { type Database } from '@formidablejs/framework';
import { UserFactory } from '../factories/UserFactory';

export const seed = async (DB: Database): Promise<void> => {
await UserFactory.factory(5).create()
}

Factory States

Factories support states that allow you to create variations of your models with different attributes. This is particularly useful for testing different scenarios or creating specific data sets.

Here's an example of a UserFactory with states:

database/factories/UserFactory.ts
import { Factory, Hash } from "@formidablejs/framework";

export class UserFactory extends Factory {
protected password: string;

async definition(): Promise<any> {
return {
name: this.faker().person.fullName(),
email: this.faker().internet.email(),
email_verified_at: now(),
password: this.password ?? await Hash.make('password'),
remember_token: encrypt(strRandom(80)),
};
}

// State for unverified users
unverified() {
return this.state({
email_verified_at: null
});
}

// State for admin users
admin() {
return this.state({
role: 'admin',
email: 'admin@example.com'
});
}

// State for users with specific attributes
withRole(role: string) {
return this.state({
role: role
});
}
}

Using factory states in your seeders:

database/seeders/DatabaseSeeder.ts
import { type Database } from '@formidablejs/framework';
import { UserFactory } from '../factories/UserFactory';

export const seed = async (DB: Database): Promise<void> => {
// Create 5 regular users
await UserFactory.factory(5).create();

// Create 3 unverified users
await UserFactory.factory(3).unverified().create();

// Create 1 admin user
await UserFactory.factory(1).admin().create();

// Create 2 moderators
await UserFactory.factory(2).withRole('moderator').create();
}

Using Direct Insert Queries

Here's an example of a users seeder that deletes all the users before adding new ones using direct insert queries:

database/seeders/UsersSeeder.ts
import { type Database, Hash } from '@formidablejs/framework';
import { strRandom } from '@formidablejs/framework/lib/Support/Helpers';

export const seed = async (DB: Database): Promise<void> => {
// Deletes ALL existing entries
await DB.table('users').del();

const password = await Hash.make('password');

// Inserts seed entries
await DB.table('users').insert([
{
name: strRandom(10),
email: strRandom(10) + '@gmail.com',
password: password,
},
]);
};

Advanced Seeding

Seeding with Relationships

Here's an example that seeds related data:

database/seeders/PostsSeeder.ts
import { type Database } from '@formidablejs/framework';

export const seed = async (DB: Database): Promise<void> => {
// First, get a user to associate posts with
const users = await DB.table('users').select('id').limit(1);

if (users.length > 0) {
const userId = users[0].id;

// Insert posts with foreign key relationship
await DB.table('posts').insert([
{
user_id: userId,
title: 'My First Post',
body: 'This is the content of my first post.',
created_at: new Date(),
updated_at: new Date(),
},
{
user_id: userId,
title: 'Another Post',
body: 'This is another post with some content.',
created_at: new Date(),
updated_at: new Date(),
},
]);
}
};

Seeding with Raw Queries

You can also use raw SQL queries for complex seeding scenarios:

database/seeders/ComplexSeeder.ts
import { type Database } from '@formidablejs/framework';

export const seed = async (DB: Database): Promise<void> => {
// Using raw SQL for complex operations
await DB.raw(`
INSERT INTO categories (name, slug, created_at, updated_at)
VALUES
('Technology', 'technology', NOW(), NOW()),
('Lifestyle', 'lifestyle', NOW(), NOW()),
('Business', 'business', NOW(), NOW())
ON DUPLICATE KEY UPDATE updated_at = NOW()
`);

// Using query builder for conditional inserts
const existingUsers = await DB.table('users').count('* as count').first();

if (existingUsers.count === 0) {
await DB.table('users').insert({
name: 'Admin User',
email: 'admin@example.com',
password: await Hash.make('password'),
created_at: new Date(),
updated_at: new Date(),
});
}
};

Batch Insertion

For large amounts of data, you can use batch insertion:

database/seeders/BatchSeeder.ts
import { type Database } from '@formidablejs/framework';

export const seed = async (DB: Database): Promise<void> => {
const users = [];

// Generate 1000 users
for (let i = 0; i < 1000; i++) {
users.push({
name: `User ${i + 1}`,
email: `user${i + 1}@example.com`,
password: await Hash.make('password'),
created_at: new Date(),
updated_at: new Date(),
});
}

// Insert in batches of 100
const batchSize = 100;
for (let i = 0; i < users.length; i += batchSize) {
const batch = users.slice(i, i + batchSize);
await DB.table('users').insert(batch);
}
};

Running Seeders

You may execute the db:seed Craftsman command to seed your database. This command will run all your seeders:

node craftsman db:seed

Running Specific Seeders

You can also run specific seeders by providing the seeder name:

node craftsman db:seed --seeder=UsersSeeder

Refreshing and Seeding

To refresh your database and run all seeders:

node craftsman migrate:fresh --seed