Table Of Contents
- Laravel's Built-in Solution: Custom Seeders
- Performance-Optimized CSV Import
- Advanced CSV Import with Validation
Laravel's Built-in Solution: Custom Seeders
Got a CSV file with thousands of records to import? Laravel seeders combined with CSV parsing make this painless:
// database/seeders/UsersFromCsvSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use League\Csv\Reader;
class UsersFromCsvSeeder extends Seeder
{
public function run()
{
// Read CSV file
$csv = Reader::createFromPath(database_path('seeders/data/users.csv'), 'r');
$csv->setHeaderOffset(0); // First row contains headers
$records = $csv->getRecords();
foreach ($records as $record) {
DB::table('users')->insert([
'name' => $record['name'],
'email' => $record['email'],
'email_verified_at' => now(),
'password' => bcrypt($record['password'] ?? 'password'),
'created_at' => now(),
'updated_at' => now(),
]);
}
}
}
Install League CSV for better CSV handling:
composer require league/csv
Performance-Optimized CSV Import
For large CSV files, use chunking and batch inserts to avoid memory issues:
// High-performance CSV seeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use League\Csv\Reader;
class LargeDatasetSeeder extends Seeder
{
public function run()
{
$csv = Reader::createFromPath(storage_path('app/import/products.csv'), 'r');
$csv->setHeaderOffset(0);
$batchSize = 1000;
$batch = [];
foreach ($csv->getRecords() as $record) {
$batch[] = [
'name' => $record['product_name'],
'description' => $record['description'],
'price' => (float) $record['price'],
'category_id' => $this->getCategoryId($record['category']),
'created_at' => now(),
'updated_at' => now(),
];
// Insert in batches for better performance
if (count($batch) >= $batchSize) {
DB::table('products')->insert($batch);
$batch = []; // Reset batch
$this->command->info("Imported batch of {$batchSize} records");
}
}
// Insert remaining records
if (!empty($batch)) {
DB::table('products')->insert($batch);
$this->command->info("Imported final batch of " . count($batch) . " records");
}
}
private function getCategoryId($categoryName)
{
// Cache categories to avoid repeated queries
static $categories = null;
if ($categories === null) {
$categories = DB::table('categories')->pluck('id', 'name')->toArray();
}
return $categories[$categoryName] ?? 1; // Default category
}
}
Advanced CSV Import with Validation
Add data validation and error handling for production imports:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use League\Csv\Reader;
class ValidatedCsvSeeder extends Seeder
{
public function run()
{
$csv = Reader::createFromPath(database_path('seeders/customers.csv'), 'r');
$csv->setHeaderOffset(0);
$successCount = 0;
$errorCount = 0;
$errors = [];
DB::transaction(function () use ($csv, &$successCount, &$errorCount, &$errors) {
foreach ($csv->getRecords() as $offset => $record) {
// Validate each record
$validator = Validator::make($record, [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'phone' => 'nullable|string|max:20',
'city' => 'required|string|max:100',
]);
if ($validator->fails()) {
$errorCount++;
$errors[] = "Row " . ($offset + 2) . ": " . implode(', ', $validator->errors()->all());
continue;
}
try {
DB::table('users')->insert([
'name' => $record['name'],
'email' => strtolower(trim($record['email'])),
'phone' => $record['phone'] ?? null,
'city' => $record['city'],
'email_verified_at' => now(),
'password' => bcrypt('default_password'),
'created_at' => now(),
'updated_at' => now(),
]);
$successCount++;
} catch (\Exception $e) {
$errorCount++;
$errors[] = "Row " . ($offset + 2) . ": Database error - " . $e->getMessage();
}
}
});
// Report results
$this->command->info("Import completed:");
$this->command->info("✅ Successful: {$successCount} records");
$this->command->info("❌ Failed: {$errorCount} records");
if (!empty($errors)) {
$this->command->error("Errors encountered:");
foreach (array_slice($errors, 0, 10) as $error) { // Show first 10 errors
$this->command->error($error);
}
}
}
}
Run your CSV seeder:
# Run specific seeder
php artisan db:seed --class=UsersFromCsvSeeder
# Run all seeders
php artisan db:seed
# Fresh migration + seeding
php artisan migrate:fresh --seed
CSV structure example (database/seeders/data/users.csv
):
name,email,phone,city
John Doe,john@example.com,+1234567890,New York
Jane Smith,jane@example.com,+0987654321,Los Angeles
For production imports, always use transactions, validate data, handle errors gracefully, and process large files in chunks. Consider using upsert()
for handling duplicate records or Laravel Excel package for more complex spreadsheet imports.
Related: Laravel Collections: Beyond Basic Array Operations | Database Design: Fundamentals of Good Database Architecture | Database Design Patterns: Complete Guide 2025 | Laravel Factories and Seeders: Test Data the Right Way | Database Design: Fundamentals of Good Database Architecture
Add Comment
No comments yet. Be the first to comment!