PHPackages                             iz-ahmad/laravel-turbo-seeder - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. iz-ahmad/laravel-turbo-seeder

ActiveLibrary[Database &amp; ORM](/categories/database)

iz-ahmad/laravel-turbo-seeder
=============================

Blazing fast database seeder for Laravel - seed millions of records in minutes, or even seconds!

40PHPCI passing

Since Apr 29Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/iz-ahmad/laravel-turbo-seeder)[ Packagist](https://packagist.org/packages/iz-ahmad/laravel-turbo-seeder)[ RSS](/packages/iz-ahmad-laravel-turbo-seeder/feed)WikiDiscussions master Synced 2d ago

READMEChangelogDependenciesVersions (2)Used By (0)

Laravel Turbo Seeder
====================

[](#laravel-turbo-seeder)

[![Tests](https://github.com/iz-ahmad/laravel-turbo-seeder/actions/workflows/run-tests.yml/badge.svg)](https://github.com/iz-ahmad/laravel-turbo-seeder/actions/workflows/run-tests.yml) [![PHP Version](https://camo.githubusercontent.com/1a9cfa11fa92cc87e3e433eb620749fe16de26bc22a29a651bc35decc47e4822/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344253230382e322d626c75652e737667)](https://php.net) [![Laravel Version](https://camo.githubusercontent.com/d577dd0826559cde355c1a3b4f5732fc023921472811ed5bc0a68ade0958dc83/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d31302d2d31332d7265642e737667)](https://laravel.com)

**Blazing fast database seeder for Laravel - seed millions of records in seconds; not minutes.**

Laravel Turbo Seeder is a high-performance seeding package built for large-scale data generation (1M+ records) with very minimal time and memory usage. Ideal for testing applications with production-sized datasets.

[![Laravel Turbo Seeder Demo](images/banner.png)](images/banner.png)

---

Why Turbo Seeder?
-----------------

[](#why-turbo-seeder)

Default Laravel seeders don’t scale well. When seeding 500K–1M+ records for realistic performance testing, they can consume too much time and slow down development.

**Turbo Seeder eliminates that bottleneck.**

What used to take **~30 minutes** for **1M records** now completes in **~15–60 seconds**.

No more coffee breaks, tab-switching, or "I'll test later"! So you can:

- Test against production-scale datasets
- Detect slow queries and indexing issues early
- Iterate faster without waiting on long seeding cycles

How It’s So Fast
----------------

[](#how-its-so-fast)

1. **No Eloquent overhead**: raw queries only (no model events, no Faker)
2. **Bulk inserts**: multi-row `INSERT` instead of row-by-row
3. **Native CSV imports**: `LOAD DATA` / `COPY` for maximum throughput
4. **Smart chunking**: controlled memory with automatic garbage collection
5. **Minimal overhead**: foreign key checks &amp; query logging disabled automatically
6. **Streaming I/O**: CSV handled via streams, not loaded fully into memory

---

Features At A Glance
--------------------

[](#features-at-a-glance)

- **Lightning Fast**: 1M records in 15–60 seconds (table-complexity dependent)
- **Memory Efficient**: under 200MB peak
- **Multi-Database**: MySQL, PostgreSQL, SQLite
- **Two Strategies**: bulk insert or native CSV import
- **Fluent API**: clean, chainable interface
- **TurboData Helpers**: Faker-free data generation: weighted picks, date ranges, unique values
- **Data Type Handling**: automatically formats enums, JSON, dates, collections, and objects.
- **Relational Seeding**: load FK values from seeded tables in one line, zero extra queries
- **Progress Tracking**: real-time progress with metrics
- **Highly Configurable**: chunk size, transactions, upserts, retries, dry-run, etc.
- **Laravel 10–13 Compatible**

### Ideal For

[](#ideal-for)

- Performance and load testing with large datasets
- Dev environments needing production-scale data generation
- CI/CD pipelines requiring fast seeding
- Query and database performance benchmarking

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
    - [Basic Usage](#basic-usage)
    - [CSV Strategy (Fastest)](#csv-strategy-fastest)
    - [Advanced Configuration](#advanced-configuration)
- [Common Use Cases](#common-use-cases)
- [Strategy Comparison](#strategy-comparison)
- [CSV Strategy Setup](#csv-strategy-setup)
    - [Troubleshooting](#troubleshooting)
- [Migration from Standard Seeders](#migration-from-standard-seeders)
- [API Documentation](#api-documentation)
- [Configuration Reference](#configuration-reference)
- [Architecture Overview](#architecture-overview)
- [Performance Benchmarks](#performance-benchmarks)
- [Others](#testing)

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10.x, 11.x, 12.x, or 13.x
- MySQL 5.7+, PostgreSQL 9.6+, or SQLite 3.24+

---

Installation
------------

[](#installation)

```
composer require iz-ahmad/laravel-turbo-seeder
```

The package auto-registers itself. Optionally publish the config:

```
php artisan vendor:publish --tag="turbo-seeder-config"
```

This creates `config/turbo-seeder.php` in your project.

> **Note:** This package is not publicly released yet. So for now, you can use it locally by cloning the repository and installing it in your Laravel application via a path repository in `composer.json`.

### Local Installation

[](#local-installation)

1. Clone the repository somewhere on your machine:

```
git clone https://github.com/iz-ahmad/laravel-turbo-seeder.git
```

2. In your Laravel project's `composer.json`, add:

```
{
    "repositories": [
        {
            "type": "path",
            "url": "../path-to-laravel-turbo-seeder"
        }
    ]
}
```

3. Then require it:

```
composer require iz-ahmad/laravel-turbo-seeder:@dev
```

---

Quick Start
-----------

[](#quick-start)

No extra configuration required to get started.
The default strategy works out of the box with sensible, performance-optimized settings **already configured** for you. So you just have to:

Install → write your generator → run.

### Basic Usage

[](#basic-usage)

```
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

$uniqueEmail = TurboData::uniqueEmail();

TurboSeeder::create('users')
    ->columns(['name', 'email', 'password', 'created_at'])
    ->generate(fn ($index) => [
        'name'       => "User {$index}",
        'email'      => $uniqueEmail($index),
        'password'   => TurboData::hashedPassword(),
        'created_at' => TurboData::nowOnce(),
    ])
    ->count(100000)
    ->run();
```

### CSV Strategy (Fastest)

[](#csv-strategy-fastest)

```
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

TurboSeeder::create('posts')
    ->columns(['user_id', 'title', 'content', 'created_at'])
    ->generate(fn ($index) => [
        'user_id'    => TurboData::randomInt(1, 10000),
        'title'      => "Post {$index}",
        'content'    => "Content for post {$index}",
        'created_at' => TurboData::nowOnce(),
    ])
    ->count(1000000)
    ->useCsvStrategy()
    ->run();
```

### Advanced Configuration

[](#advanced-configuration)

```
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

TurboSeeder::create('orders')
    ->columns(['user_id', 'total', 'status', 'created_at'])
    ->generate(fn ($index) => [
        'user_id'    => TurboData::randomInt(1, 10000),
        'total'      => TurboData::randomFloat(2, 10.00, 999.99),
        'status'     => TurboData::weightedFrom(['pending' => 50, 'completed' => 40, 'cancelled' => 10]),
        'created_at' => TurboData::dateRange('2023-01-01', '2024-12-31'),
    ])
    ->count(50000)
    ->chunkSize(3000)
    ->withProgressTracking()
    ->disableForeignKeyChecks()
    ->connection('mysql')
    ->run();
```

See [src/Examples/ExampleSeeder.php](src/Examples/ExampleSeeder.php) and [Common Use Cases](#common-use-cases) for more examples.

---

Common Use Cases
----------------

[](#common-use-cases)

### Seeding Tables with Relationships

[](#seeding-tables-with-relationships)

Create users with related posts using `fromTable()` for clean FK assignment:

```
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

// Seed users first
TurboSeeder::create('users')
    ->columns(['name', 'email', 'created_at'])
    ->generate(fn ($index) => [
        'name'       => "User {$index}",
        'email'      => "user{$index}@example.com",
        'created_at' => TurboData::nowOnce(),
    ])
    ->count(50000)
    ->run();

// fromTable() loads user IDs once from the DB, then cycles deterministically
$userIds = TurboData::fromTable('users');

// seed posts - each post gets a valid user_id with zero extra DB queries
TurboSeeder::create('posts')
    ->columns(['user_id', 'title', 'content', 'created_at'])
    ->generate(fn ($index) => [
        'user_id'    => $userIds($index),
        'title'      => "Post {$index}",
        'content'    => "Content for post {$index}",
        'created_at' => TurboData::dateRange('2023-01-01', '2024-12-31'),
    ])
    ->count(100000)
    ->useCsvStrategy()
    ->run();
```

### Creating Time-Series Data

[](#creating-time-series-data)

Generate sequential data for analytics:

```
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

TurboSeeder::create('analytics_events')
    ->columns(['event_type', 'value', 'recorded_at'])
    ->generate(fn ($index) => [
        'event_type'  => TurboData::cycleFrom(['page_view', 'click', 'signup'])($index),
        'value'       => TurboData::randomInt(1, 100),
        'recorded_at' => TurboData::sequentialDate('2024-01-01', 'hour', $index),
    ])
    ->count(8760) // One year of hourly data
    ->run();
```

### Performance Testing Scenarios

[](#performance-testing-scenarios)

Test your application with realistic data volumes:

```
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

// Simulate e-commerce orders
TurboSeeder::create('orders')
    ->columns(['user_id', 'total', 'status', 'created_at'])
    ->generate(fn ($index) => [
        'user_id'    => TurboData::randomInt(1, 50000),
        'total'      => TurboData::randomFloat(2, 10.00, 999.99),
        'status'     => TurboData::weightedFrom(['pending' => 30, 'completed' => 60, 'cancelled' => 10]),
        'created_at' => TurboData::dateRange('2023-01-01', '2024-12-31'),
    ])
    ->count(500000)
    ->chunkSize(2000)
    ->withProgressTracking()
    ->run();
```

### CI/CD Integration

[](#cicd-integration)

Use in your CI/CD pipeline for fast test data setup:

```
# In your CI/CD script
php artisan migrate:fresh --seed
php artisan turbo-seeder:run PerformanceTestSeeder
php artisan test
```

---

Strategy Comparison
-------------------

[](#strategy-comparison)

FeatureDefault StrategyCSV Strategy**Speed**Fast (~15-60s for 1M)Fastest (~9-40s for 1M)¹**Memory**Moderate (~50-160 MB)Minimal (~0 MB additional)**Setup**No configuration requiredRequires some database config**Best For**Remote databases, general useLocal databases, max speed**Compatibility**All databasesMySQL, PostgreSQL, SQLite¹ **SQLite Note:** CSV strategy may be *slower* than default strategy on SQLite due to file I/O overhead. CSV shines mainly on MySQL (`LOAD DATA`) and PostgreSQL (`COPY`).

**Recommendation**:

- **MySQL/PostgreSQL**: Use CSV strategy for 1M+ records
- **SQLite**: Use default strategy
- **General use**: Start with default. Switch to CSV strategy for maximum speed on local databases.

CSV Strategy Setup
------------------

[](#csv-strategy-setup)

The CSV strategy provides the fastest seeding performance but requires additional database configuration.

### Automatic Fallback

[](#automatic-fallback)

If CSV strategy is not properly configured, TurboSeeder will **automatically fall back** to the default (bulk insert) strategy. You'll see a warning message with instructions, but seeding will continue successfully.

### MySQL Configuration

[](#mysql-configuration)

To enable CSV strategy for MySQL, add `PDO::MYSQL_ATTR_LOCAL_INFILE` to your database connection options:

```
// config/database.php
'mysql' => [
    'driver' => 'mysql',
    // ... other settings ...
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        PDO::MYSQL_ATTR_LOCAL_INFILE => true,  // Add this line
    ]) : [],
],
```

**MySQL server-side requirement:** The above enables the client side. MySQL also requires `local_infile` to be enabled on the server — it defaults to `OFF` in MySQL 8.0 and later. Enable it at runtime: `SET GLOBAL local_infile = 1;` or permanently via `local_infile = 1` under `[mysqld]` in your MySQL config file.

**Security Note:** `LOAD DATA LOCAL INFILE` allows MySQL to read files from the client machine. Only enable this in trusted environments (development, staging). Consider disabling in production unless absolutely necessary.

### PostgreSQL Configuration

[](#postgresql-configuration)

For PostgreSQL, the CSV strategy uses the `COPY` command which requires:

1. **File Access** - PostgreSQL server must have read access to `storage/app/turbo-seeder/`
2. **User Privileges** - Database user must have `COPY` privileges on target tables
3. **Server Location** - For remote servers, ensure CSV file path is accessible

**Note:** For local PostgreSQL installations, CSV strategy typically works without additional configuration. For remote servers, you may need network file sharing or use the default strategy.

### SQLite Configuration

[](#sqlite-configuration)

SQLite supports CSV strategy but has different performance characteristics:

**Performance Note:** Due to SQLite's file-based architecture and file I/O overhead, the CSV strategy sometimes may be **slower** than the default bulk insert strategy. So, for SQLite development, use the **default strategy** unless you specifically find CSV beneficial for your use case.

### Troubleshooting

[](#troubleshooting)

If you see a warning about CSV strategy falling back to default:

1. **MySQL** - Verify `PDO::MYSQL_ATTR_LOCAL_INFILE => true` is in `config/database.php`
2. **PostgreSQL** - Check file permissions and COPY privileges
3. **All** - Review application logs for detailed error messages

The **default** strategy works *without* any additional configuration and is still very fast.

---

Migration from Standard Seeders
-------------------------------

[](#migration-from-standard-seeders)

Converting existing Laravel seeders to use Turbo Seeder is straightforward:

### Before (Standard Laravel Seeder)

[](#before-standard-laravel-seeder)

```
use Illuminate\Database\Seeder;
use App\Models\User;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        User::factory()->count(10000)->create();
    }
}
```

### After (Turbo Seeder)

[](#after-turbo-seeder)

```
use Illuminate\Database\Seeder;
use IzAhmad\TurboSeeder\Facades\TurboSeeder;
use IzAhmad\TurboSeeder\Helpers\TurboData;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        $uniqueEmail = TurboData::uniqueEmail();

        TurboSeeder::create('users')
            ->columns(['name', 'email', 'password', 'created_at'])
            ->generate(fn ($i) => [
                'name'       => "User {$i}",
                'email'      => $uniqueEmail($i),
                'password'   => TurboData::hashedPassword(),
                'created_at' => TurboData::nowOnce(),
            ])
            ->count(10000)
            ->run();
    }
}
```

---

API Documentation
-----------------

[](#api-documentation)

### Fluent API Methods

[](#fluent-api-methods)

#### Core Methods

[](#core-methods)

- `table(string $table)` - Set the table name. Accepts plain names (`users`) and schema-qualified names (`public.users`, `myschema.my_table`). Names must start with a letter or underscore and contain only letters, digits, and underscores. **SQLite note:** schema-qualified names require an [ATTACHed database](https://www.sqlite.org/lang_attach.html) alias; without ATTACH the query will fail at runtime with "no such table".
- `columns(array $columns)` - Set columns to seed
- `generate(Closure $generator)` - Set data generator function
- `count(int $count)` - Set number of records to seed
- `run()` - Execute the seeding operation

#### Strategy Methods

[](#strategy-methods)

- `useCsvStrategy()` - Native CSV file import (fastest)
- `useDefaultStrategy()` - Bulk INSERT (default)
- `strategy(SeederStrategy $strategy)` - Set via enum directly

#### Configuration Methods

[](#configuration-methods)

- `connection(string $connection)` - Database connection to use
- `chunkSize(int $size)` - Records per chunk
- `withProgressTracking()` / `withoutProgressTracking()` - Toggle progress bar
- `disableForeignKeyChecks()` / `enableForeignKeyChecks()` - Toggle FK checks
- `disableQueryLog()` / `enableQueryLog()` - Toggle query logging
- `useTransactions()` / `withoutTransactions()` - Toggle transactions
- `options(array $options)` - Merge custom options
- `when(bool|callable $condition, callable $callback, ?callable $default)` - Conditional chaining
- `unless(bool|callable $condition, callable $callback, ?callable $default)` - Inverse conditional

#### Advanced Methods

[](#advanced-methods)

- `dryRun(bool $enabled = true)` - Generate and validate data without committing. Uses transaction rollback; `$result->isDryRun` will be `true`.

> **Do not combine this with `withoutTransactions()`**; because without a transaction, there is nothing to roll back and rows will be permanently written.

- `upsert(array $uniqueBy)` - On conflict, update non-key columns. Uses `ON DUPLICATE KEY UPDATE` (MySQL), `ON CONFLICT DO UPDATE SET` (PostgreSQL / SQLite 3.24+). Keys must be a subset of declared columns and must form a unique constraint on the table.
- `retryAttempts(int $attempts)` - Retry on transient deadlock / lock-timeout failures (SQLSTATE 40001, MySQL 1205) with exponential backoff. Accepts 1–10; defaults to 3.
- `withoutColumnValidation()` - Skip the pre-seed schema check that validates declared columns exist on the table.

#### Events

[](#events)

`TurboSeederCompleted` is dispatched after every successful seed, including dry-runs; which you can use to trigger actions after seeding as per your requirements.

```
use IzAhmad\TurboSeeder\Events\TurboSeederCompleted;

class SendTurboSeederCompletedNotification
{
    /**
     * Handle the event.
     */
    public function handle(TurboSeederCompleted $event): void
    {
        // $event->table  - the seeded table name
        // $event->result - SeederResultDTO (includes isDryRun flag)

        if ($event->result->isDryRun) {
            return; // no rows were committed
        }
    }
}
```

> **Note:** Always check `$event->result->isDryRun` before acting on the assumption that rows were committed. The event is **not** dispatched when seeding fails.

---

### TurboData Helpers

[](#turbodata-helpers)

`TurboData` is a Faker-free data generation utility designed for high-volume seeding. Every method is safe to call 1M+ times.

> All returned values are automatically formatted via the internal **ValueFormatter**.

```
use IzAhmad\TurboSeeder\Helpers\TurboData;
```

#### Value Selection

[](#value-selection)

```
// Round-robin cycling
$role = TurboData::cycleFrom(['admin', 'editor', 'viewer']); // returns a closure: $role($index)

// Weighted random
$status = TurboData::weightedFrom(['active' => 70, 'pending' => 20, 'banned' => 10]); // returns value directly

// Uniform random
$method = TurboData::randomFrom(['paypal', 'bank_transfer', 'credit_card']);
```

#### Scalars

[](#scalars)

```
$age   = TurboData::randomInt(18, 65);
$price = TurboData::randomFloat(2, 9.99, 999.99);
$flag  = TurboData::randomBool(0.8); // 80% true
```

#### Dates &amp; Timestamps

[](#dates--timestamps)

```
// Random date within a range
$date = TurboData::dateRange('2022-01-01', '2024-12-31');

// Sequential timestamps - good for time-series data
$ts = TurboData::sequentialDate('2024-01-01', 'hour', $index);

// Use nowOnce() inside generators for better performance - avoids calling now() 1M times
'created_at' => TurboData::nowOnce()

// Hash once, reuse across all records - never call bcrypt() inside the generator
'password' => TurboData::hashedPassword()          // default: 'password'
'password' => TurboData::hashedPassword('secret')  // custom password
```

#### Nullable Values

[](#nullable-values)

```
// 15% chance of null; value only evaluated when not null
$deletedAt = TurboData::nullable(0.15, fn () => now());
```

#### Seeding Related Tables

[](#seeding-related-tables)

**`fromTable()`** is the standard way to assign FK values. It plucks a column from an already-seeded table once, caches it in memory, then cycles or randomly picks from it on every generator call, ensuring zero extra DB queries after the first.

```
$userIds     = TurboData::fromTable('users');                      // cycle (default)
$categoryIds = TurboData::fromTable('categories', 'id', 'random'); // random pick
$codes       = TurboData::fromTable('regions', 'code', 'cycle', 'reports'); // custom column + connection

TurboSeeder::create('posts')
    ->columns(['user_id', 'category_id', 'title'])
    ->generate(fn ($i) => [
        'user_id'     => $userIds($i),
        'category_id' => $categoryIds($i),
        'title'       => "Post {$i}",
    ])
    ->count(1_000_000)
    ->run();
```

> Seed the referenced table **before** calling `fromTable()`. The DB query fires once on the first generator call; all subsequent calls are O(1) array lookups.

**`fromQuery()`** - use this when `fromTable()` isn't enough: custom filters, joins, specific ordering, or any query that can't be expressed as a simple column pluck.

```
// Only referencing `active` users; fromTable() can't filter, but fromQuery() can
$userIds = TurboData::fromQuery(
    fn () => DB::table('users')->where('active', 1)->orderBy('id')->pluck('id')->toArray()
);
```

`fromQuery()` accepts any callable that returns an array. Same lazy-load and cycle semantics as the `fromTable()` (loaded once, then cycled by index).

#### Unique Values

[](#unique-values)

```
$email = TurboData::uniqueEmail();         // u_a3f9b2c1_0@turbo.test
$user  = TurboData::uniqueUsername('usr'); // usr_a3f9b2c1_0
$slug  = TurboData::uniqueSlug('My Post'); // my-post-a3f9b2c1-0
$uuid  = TurboData::uniqueUuid('ref_');    // ref_xxxxxxxx-xxxx-...
// All return closures: $email($index)
```

---

### Data Type Handling

[](#data-type-handling)

TurboSeeder **automatically formats** all types of values returned from your generator via **ValueFormatter**. You don’t need to manually convert data types; everything is handled internally.

#### Supported Types

[](#supported-types)

Input TypeStored As`null``NULL``bool``1` / `0``int`, `float`, `string`unchanged`json` (string)stored as-is`DateTime` / `Carbon``Y-m-d H:i:s``BackedEnum`enum value`UnitEnum`enum name`array`JSON string`Collection`JSON string`object` / `stdClass`JSON string#### JSON Handling Example

[](#json-handling-example)

```
TurboSeeder::create('posts')
    ->columns(['data', 'metadata'])
    ->generate(fn ($i) => [
        // PHP array - automatically JSON encoded
        'data' => ['nested' => ['key' => 'value']],

        // JSON string - stored as-is (no double encoding)
        'metadata' => '{"source":"api"}',
    ])
    ->count(1000)
    ->run();
```

**Result in database:**

- `data` → `{"nested":{"key":"value"}}`
- `metadata` → `{"source":"api"}`

#### Custom Type Formatters

[](#custom-type-formatters)

You can even register custom formatters for your own value objects, if you need to:

```
use IzAhmad\TurboSeeder\Services\ValueFormatter;

// In a service provider
ValueFormatter::extend(
    Money::class,
    fn ($money) => $money->getAmount()
);
```

Now any `Money` object returned from your generator will be formatted automatically.

#### Manual Formatting

[](#manual-formatting)

You won't need to manually format values, since TurboSeeder does it automatically. Only use `ValueFormatter` manually if you need to validate or format outside the generator context:

```
use IzAhmad\TurboSeeder\Services\ValueFormatter;

ValueFormatter::format($value);
ValueFormatter::formatForCsv($value, '\\N');
```

**Key Behaviors:**

- Fully automatic - no manual conversions required
- Type-safe - preserves scalar types and safely converts complex types
- JSON-safe - no double encoding
- CSV-compatible
- Extensible for custom value objects

---

### Artisan Commands

[](#artisan-commands)

#### Run a Seeder

[](#run-a-seeder)

```
php artisan turbo-seeder:run YourSeederClass
```

**Arguments:**

- `seeder` - The seeder class name

**Options:**

- `--class=` - Seeder class name (no need if you use the `seeder` argument)

You can still use Laravel’s native `php artisan db:seed` command when using this package. *However*, the `turbo-seeder:run` command provided by this package offers **additional benefits**: easily **customize** options, view detailed **performance metrics**, and monitor real-time **progress**; making it ideal for large-scale or advanced seeding operations.

#### Benchmark Performance

[](#benchmark-performance)

```
php artisan turbo-seeder:benchmark [--connection=] [--table=] [--records=]
```

**Options:**

- `--connection=` - Database connection
- `--table=` - Table name (default: benchmark\_test)
- `--records=` - Number of records (default: 10000)

#### Test Connection

[](#test-connection)

```
php artisan turbo-seeder:test-connection
```

#### Clear Cache

[](#clear-cache)

```
php artisan turbo-seeder:clear-cache [--all]
```

**Options:**

- `--all` - Clear all temporary files including subdirectories created during seeding.

---

Configuration Reference
-----------------------

[](#configuration-reference)

We have provided an optimal configuration for you to use. Still, you can publish and customize the config for full control:

```
php artisan vendor:publish --tag="turbo-seeder-config"
```

### Chunk Sizes

[](#chunk-sizes)

Chunk size determines how many records are inserted (processed in memory) at once. This directly impacts memory usage and performance.

**Config Priority Order:**

1. **Custom chunk size** (set via `->chunkSize()` in the seeder class using TurboSeeder fluent API) - gets **Highest** priority
2. **Database-specific chunk size** (from `chunk_sizes.{database_driver}` config) - gets **Medium** priority
3. **Default chunk size** (from `default_chunk_size` config) - used as Fallback

```
'default_chunk_size' => 1000, // Fallback when database-specific size not set

'chunk_sizes' => [
    'mysql' => 1000,   // Optimal for MySQL
    'pgsql' => 800,    // Optimal for PostgreSQL
    'sqlite' => 500,   // Optimal for SQLite
], // these values take priority over the default_chunk_size
```

**Why Chunk Size Matters:**

Chunk size directly affects memory consumption. Each chunk loads all records into memory before inserting them into the database. The memory usage formula is approximately:

```
Memory ≈ (chunk_size × number_of_columns × average_value_size) + overhead

```

**Key Considerations:**

- **More columns = smaller chunk size needed**: Tables with 15+ columns or large fields require smaller chunks to stay within memory limits.
- **Fewer columns = larger chunk size possible**: Simple tables (3-5 columns) can handle larger chunks efficiently.
- **Default strategy**: More memory-intensive than CSV strategy, so consider **smaller chunks for large datasets**.
- **CSV strategy**: More memory-efficient, can handle larger chunks even with many columns. Because it uses the database's **native CSV import** command.

**Recommendations for chunk size:**

- **Simple tables (3-5 columns)**: 1000 - 5000
- **Medium tables (6-10 columns)**: ~ 1000
- **Complex tables (15+ columns, large text/JSON)**: 200 - 1000
- **For very large datasets (1M+ records)**: Consider CSV strategy or reduce chunk size if memory limit is exhausted.

### Memory Management

[](#memory-management)

Configure memory limits and garbage collection:

```
'memory' => [
    'limit_mb' => 256,              // Memory limit in MB
    'gc_threshold_percent' => 80,   // Trigger GC at 80% memory usage
    'force_gc_after_chunks' => 10,  // Force GC every 10 chunks
],
```

### Performance Optimizations

[](#performance-optimizations)

Enable/disable various performance features:

```
'performance' => [
    'disable_query_log' => true,      // Disable Laravel query logging (recommended)
    'disable_foreign_keys' => true,   // Disable foreign key checks during seeding
    'use_transactions' => true,       // Wrap operations in transactions
],
```

### CSV Strategy Configuration

[](#csv-strategy-configuration)

Settings for CSV-based seeding:

```
'csv_strategy' => [
    'enabled' => true,                                   // Enable CSV strategy
    'temp_path' => storage_path('app/turbo-seeder'),     // Directory for temporary CSV files
    'buffer_size' => 8192,                               // File write buffer size (bytes)
    'line_terminator' => "\n",                           // CSV line ending
    'field_delimiter' => ',',                            // CSV field separator
    'field_enclosure' => '"',                            // CSV field enclosure
    'batch_size' => 10000,                               // Records per CSV batch
    'gc_frequency' => 5,                                 // Run GC every N batches
    'reader_chunk_size_for_sqlite' => 500,               // SQLite CSV read chunk size
    'fallback_to_default_strategy_on_config_error' => true, // Auto fallback to default strategy (bulk insert) if CSV fails due to missing configuration.
    'null_marker' => '\\N',                              // Sentinel used for NULL values in CSV files
],
```

**Key Settings:**

- `fallback_to_default_strategy_on_config_error` - Automatically switches to bulk insert if CSV import fails due to missing database configuration. Ensures seeding completes successfully.
- `null_marker` - The string written to CSV for `null` values. The default `\N` matches MySQL and PostgreSQL native CSV null conventions. Only change this if your data legitimately contains the literal string `\N`.

### Progress Tracking

[](#progress-tracking)

Configure progress bar display:

```
'progress' => [
    'enabled' => true,           // Enable progress tracking by default
    'update_frequency' => 1000,  // Update progress every 1000 records
],
```

### Error Handling

[](#error-handling)

Configure error reporting:

```
'get_error_trace_on_console' => false, // Show full stack trace in console on errors, note that errors are always fully logged to Laravel logs regardless of this setting.
'max_error_message_length_in_console' => 600, // Max characters of error message shown in console before truncation
```

### Seeder Namespace

[](#seeder-namespace)

Default namespace for seeder classes:

```
'seeder_classes_namespace' => 'Database\\Seeders\\', // Auto-resolve seeder class names
```

**Usage:** Allows using short class names in commands. For example, `php artisan turbo-seeder:run UserSeeder` instead of `php artisan turbo-seeder:run Database\\Seeders\\UserSeeder`.

---

Architecture Overview
---------------------

[](#architecture-overview)

Turbo Seeder follows a clean and efficient execution flow:

**Data Generator → Chunk Builder → Seeding Strategy → Database**

1. `generate()` produces row data.
2. Rows are grouped into memory-controlled chunks.
3. The selected strategy (Bulk Insert or CSV) processes each chunk.
4. Data is written using optimized native database operations.

Memory is controlled at the **chunk level**, with automatic garbage collection. With the **CSV strategy**, rows are streamed to temporary files (`storage/app/turbo-seeder/`) and imported via native commands (`LOAD DATA` / `COPY`), avoiding large in-memory payloads.

---

Performance Benchmarks
----------------------

[](#performance-benchmarks)

Measured on a modern local machine with **MySQL** and default chunk sizes.

### Default Strategy (Bulk Insert)

[](#default-strategy-bulk-insert)

Table complexityRecordsTimePeak memorySimple (~5 cols)1M~16s~50 MBComplex (~15–20 cols)1M~60s~160 MBBest for: general use, remote databases, when CSV import isn't available.

### CSV Strategy (File Import)

[](#csv-strategy-file-import)

Table complexityRecordsTimeAdditional memorySimple (~5 cols)1M~9s~0 MBComplex (~15–20 cols)1M~40s~0 MBBest for: local **MySQL/PostgreSQL** databases, maximum throughput where `LOAD DATA` / `COPY` can be enabled. On **SQLite**, the CSV strategy may be comparable or slower than the default strategy due to file I/O overhead.

> Results vary by hardware, DB engine/version, network latency, and chunk size.

---

Testing
-------

[](#testing)

Run the test suite to ensure everything is working correctly:

```
composer test
```

**Test Framework:** Pest PHP with SQLite, MySQL, and PostgreSQL support

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover a security issue, please email `n.ahmad.web.cit22@gmail.com` instead of opening a public issue.

Changelog
---------

[](#changelog)

Please see [CHANGELOG.md](CHANGELOG.md) for recent changes.

License
-------

[](#license)

The MIT License (MIT). Please see [LICENSE.md](LICENSE.md) for more information.

Credits
-------

[](#credits)

- All Contributors

**Made with ❤️ for the Laravel community**

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance61

Regular maintenance activity

Popularity4

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 97.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/102437433?v=4)[Nafis Ahmad](/maintainers/iz-ahmad)[@iz-ahmad](https://github.com/iz-ahmad)

---

Top Contributors

[![iz-ahmad](https://avatars.githubusercontent.com/u/102437433?v=4)](https://github.com/iz-ahmad "iz-ahmad (309 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (4 commits)")[![claude](https://avatars.githubusercontent.com/u/81847?v=4)](https://github.com/claude "claude (3 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (2 commits)")

### Embed Badge

![Health badge](/badges/iz-ahmad-laravel-turbo-seeder/health.svg)

```
[![Health](https://phpackages.com/badges/iz-ahmad-laravel-turbo-seeder/health.svg)](https://phpackages.com/packages/iz-ahmad-laravel-turbo-seeder)
```

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.9k116.5M113](/packages/jdorn-sql-formatter)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8361.6M87](/packages/propel-propel1)[mpociot/laravel-composite-key

Support composite keys in your laravel app.

3544.8k1](/packages/mpociot-laravel-composite-key)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
