Skip to content

Instantly share code, notes, and snippets.

@ibraheem-ghazi
Last active March 22, 2025 08:50
Show Gist options
  • Save ibraheem-ghazi/9f6bfcacea31e1e41c34c9b9a659bbe0 to your computer and use it in GitHub Desktop.
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+)
<?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;
}
}
@ibraheem-ghazi
Copy link
Author

🌱 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:

  • Automatic Tracking: Each successful seeder run is logged in a dedicated seeders table, preventing it from running again unless you reset it.
  • No Setup Hassle: If the 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.

Perfect for developers who want clean, non-duplicated data every time they seed!

πŸš€ Quick Start

To start using SeedGuard, simply replace extends Seeder with extends SeedGuard in your seeder class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment