Last active
March 22, 2025 08:50
-
-
Save ibraheem-ghazi/9f6bfcacea31e1e41c34c9b9a659bbe0 to your computer and use it in GitHub Desktop.
π± SeedGuard: Smart Seeding & Error Logging for Laravel β No Duplicate Data! (with support for Laravel 11+)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php /** @noinspection PhpUnhandledExceptionInspection */ | |
namespace App\Support\Libraries\SeedGuard; | |
use Exception; | |
use Illuminate\Database\Schema\Blueprint; | |
use Illuminate\Database\Seeder; | |
use Illuminate\Support\Facades\DB; | |
use Illuminate\Support\Facades\Log; | |
use Illuminate\Support\Facades\Schema; | |
use Psr\Log\LoggerInterface; | |
/** | |
* SeedGuard is a custom seeder that extends Laravel's Seeder class, adding tracking and error handling functionality. | |
* It ensures seeders are only executed once by logging them in a 'seeders' table. If any errors occur during seeding, | |
* they are logged for review. | |
*/ | |
class SeedGuard extends Seeder | |
{ | |
/** | |
* @var bool $tableEnsuredExists Tracks if the 'seeders' table existence check has been performed to avoid redundant checks. | |
*/ | |
private bool $tableEnsuredExists = false; | |
/** | |
* Ensures the existence of the 'seeders' table. If the table doesn't exist, it is created to track seeder executions. | |
* This method runs only once per instance of SeedGuard. | |
* | |
* @return void | |
*/ | |
private function ensureTableExists(): void | |
{ | |
if($this->tableEnsuredExists){ | |
return; | |
} | |
if (!Schema::hasTable('seeders')) { | |
Schema::create('seeders', function (Blueprint $table) { | |
$table->id(); | |
$table->string('seeder', 190)->unique(); | |
$table->timestamp('seeded_at')->useCurrent(); | |
}); | |
} | |
$this->tableEnsuredExists = true; | |
} | |
/** | |
* Configures and returns a logger instance for logging errors. | |
* | |
* @return LoggerInterface Returns a PSR-compliant logger set to write error logs with daily rotation. | |
*/ | |
private function getLogger(): LoggerInterface | |
{ | |
return Log::build([ | |
'driver' => 'daily', | |
'path' => storage_path('logs/seeder-errors.log'), | |
'level' => 'error', | |
'days' => 7, | |
]); | |
} | |
/** | |
* Checks if a seeder has already been executed. | |
* | |
* This method queries the `seeders` table to determine if a specific seeder | |
* has been run previously. If the seeder exists in the table, it has already | |
* been executed, and the method returns true; otherwise, it returns false. | |
* | |
* @param string $class The name of the seeder class to check. | |
* @return bool Returns true if the seeder was already seeded; false otherwise. | |
*/ | |
protected function wasSeeded($class): bool | |
{ | |
return DB::table('seeders')->where('seeder', $class)->exists(); | |
} | |
/** | |
* Enhanced call method to execute a seeder with tracking and error handling. | |
* This method ensures that the seeders table exists, runs the specified seeder | |
* within a transaction, logs any errors that occur, and records the successful | |
* execution in the seeders table. | |
* | |
* @param string $class The seeder class to run. | |
* @param bool $silent Whether to suppress console output during seeding. | |
* @param array $parameters Additional parameters to pass to the seeder. | |
* @return bool Returns true if the seeder was executed and logged successfully. | |
* @throws \Throwable Throws any exception encountered during seeder execution. | |
*/ | |
public function call($class, $silent = false, array $parameters = []): bool | |
{ | |
$this->ensureTableExists(); | |
if ($this->wasSeeded($class)) { | |
$this->command->getOutput()->writeln("<info>Skipped (already seeded):</info> {$class}"); | |
return true; | |
} | |
$result = false; | |
DB::transaction(function () use(&$result, $class, $silent, $parameters) { | |
try{ | |
parent::call($class, $silent, $parameters); | |
}catch (Exception $e){ | |
$this->getLogger()->error($e); | |
throw $e; | |
} | |
DB::table('seeders')->insert(['seeder' => $class]); | |
$result = true; | |
}); | |
return $result; | |
} | |
public function __invoke(array $parameters = []) | |
{ | |
// in Laravel 11+, its calling __invoke() not call() | |
$callback = function(){}; //no-op | |
$this->ensureTableExists(); | |
$class = static::class; | |
if ($this->wasSeeded($class)) { | |
$this->command->getOutput()->writeln("<info>Skipped (already seeded):</info> {$class}"); | |
return $class; | |
} | |
DB::transaction(function () use(&$callback, $class, $parameters) { | |
try{ | |
$callback = parent::__invoke($parameters); | |
}catch (Exception $e){ | |
$this->getLogger()->error($e); | |
throw $e; | |
} | |
DB::table('seeders')->insert(['seeder' => $class]); | |
}); | |
return $callback; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
π± SeedGuard: Smart Seeding & Error Logging for Laravel β No Duplicate Data!
Say goodbye to duplicate data! SeedGuard ensures each Laravel seeder runs only once, so you never have to worry about accidental re-seeding or duplicate records. Hereβs how it works:
seeders
table, preventing it from running again unless you reset it.seeders
table doesnβt exist, SeedGuard creates it for youβno extra setup required.π οΈ Built-In Error Logging
If an error occurs during seeding, SeedGuard quietly records it in a daily error log, allowing you to catch issues without disrupting your workflow.
π Quick Start
To start using SeedGuard, simply replace
extends Seeder
withextends SeedGuard
in your seeder class.