Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Seeder Once Laravel Package

kdabrow/seeder-once

Laravel package that runs database seeders only once. It records executed seeders in a dedicated table, so repeat deployments or db:seed calls skip already-applied seeders—ideal for safely seeding production data without duplicating rows.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install in the app that will run production/data seeders:

    composer require karoldabro/seeder-once
    

    There is nothing to migrate and typically nothing to publish — the package wires itself up via a service provider. Publish the config only if you need to change the state store location:

    php artisan vendor:publish --tag=seeder-once-config
    
  2. First place to look: the trait (or base seeder) the package exposes — in the README this is the SeederOnce trait in the Karoldabro\SeederOnce namespace. Add it to any seeder that must not run twice:

    use Illuminate\Database\Seeder;
    use Karoldabro\SeederOnce\SeederOnce;
    
    class RolesAndPermissionsSeeder extends Seeder
    {
        use SeederOnce;
    
        public function run(): void
        {
            // normal, idempotent-agnostic seeding logic
        }
    }
    
  3. First real use case: production reference data. Instead of hand-checking if (Role::count() === 0) or wrapping every seeder in updateOrCreate, tag the seeder once and let the package remember it ran. The seeder body can stay a plain insert()/create() and will still be safe to leave in your DatabaseSeeder and in deploy scripts.

  4. Verify it works: run php artisan db:seed --class=RolesAndPermissionsSeeder, confirm rows, run it again. The second run should short-circuit with no duplicate rows. Then confirm where the marker was written (see Gotchas) so you know what your deploy pipeline must persist.

Implementation Patterns

  • Deployment pipeline pattern (the main one). Keep DatabaseSeeder calling your seeders as usual, then in CI/CD run php artisan migrate --force && php artisan db:seed --force. Migrations are naturally once-only; this package makes the seeding half match, so the whole step becomes safely re-runnable on every deploy. This is the biggest practical win — no more "did someone already seed roles in prod?".

  • Split "reference" vs "demo" seeders. Use SeederOnce on reference data (roles, permissions, countries, plans, settings, feature flags). Do not use it on factories/demo seeders — you want those to run repeatedly in local dev. Keeping the two in separate classes is cleaner than conditional logic inside one seeder.

  • Explicit --class invocation for one-offs. Large apps often stop using the aggregate db:seed in production and instead run named seeders in deploy scripts (php artisan db:seed --class=PlansSeeder). The trait makes each of those individually idempotent, so adding a new one to the script is safe without worrying about the others.

  • Combine with updateOrCreate for evolving data. The trait answers "has this class run?" — not "is the data current?". For records that change over time (pricing tiers, permission lists), keep updateOrCreate in the body and use the trait to prevent duplicate inserts of the initial set. Better still: for purely derived data, skip the trait and write it idempotently; use SeederOnce for appends where duplicate inserts are the actual risk.

  • Multi-tenant / per-database seeding. If you seed per tenant, the marker is keyed by seeder class only, so a second tenant will be skipped unless the package scopes state per database (check the config). The safe pattern is to set the tenant connection before the tracker is checked, or to avoid the trait in per-tenant loops and drive idempotency from the tenant's own data.

  • Testing. In feature tests that use RefreshDatabase, the database is rolled back but the once-marker may persist. Add a reset in setUp()/tearDown() (via the package's artisan reset command or by deleting/resetting the state file) so seeders actually run in each test.

Gotchas and Tips

  • The marker is not in the database (by default). It lives in a file/state store under the app's storage or config path. That means:

    • Ephemeral containers, fresh CI runners, and some PaaS filesystems will lose the marker, so "once" silently becomes "every deploy". If your deploy target has an ephemeral filesystem, point the config at a persisted path or accept that the seeder must be idempotent anyway.
    • Clearing storage or php artisan optimize:clear-adjacent cleanups can reset seeding state unintentionally. Know where the file is (the config path) and treat it like a migration record.
  • It tracks the class, not the data. Renaming/moving a seeder class makes it "never run" again. Namespacing matters: two classes with the same short name in different namespaces must still resolve distinctly — verify the tracker stores the FQCN, not the basename.

  • Overriding run() incorrectly is the classic failure. The trait must be able to wrap or gate your logic. If you define run() in a way that bypasses the trait's check (e.g. calling a custom method directly, or subclassing and overriding without parent::run()), the guard is skipped entirely. Follow the README's exact usage; don't call the underlying method from another seeder.

  • --force and --class still go through the guard. In production a skipped seeder looks like a no-op — no output, exit 0. If data seems missing after a deploy, check the marker before debugging the seeder body. A verbose run or the package's own status command (list artisan commands with php artisan list to find it) is the fastest diagnostic.

  • Concurrency. Two deploy jobs seeding simultaneously can both pass the "has it run?" check. If you deploy in parallel (e.g. multiple app servers running post-deploy hooks), serialize the seeding step or rely on database-level uniqueness in the seeder as a backstop. Don't treat the marker as a distributed lock unless the package's docs say it uses one.

  • Ordering and dependencies are unchanged. SeederOnce doesn't help with foreign-key order between seeders — RolesSeeder must still run before AssignRoleToAdminSeeder. If a once-seeder fails halfway, the marker semantics on failure matter: confirm whether the marker is written before or after run() succeeds. If it's written before, a failed seed will never retry — check this explicitly, it's the most damaging silent bug in this class of package.

  • Not a replacement for migrations. Structural/data-shape changes belong in migrations. Use this only for seed content. Teams that start seeding reference data via seeders and then need to change that data later should add a migration rather than editing the once-seeder and hoping it re-runs.

  • Extension point: the config (state store path/driver) is the main knob. If your environment can't persist the default store, that's the file to change — and it's the one thing to document in your deploy runbook so the "once" guarantee survives infrastructure changes.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core