{"id":620,"date":"2025-09-02T11:45:22","date_gmt":"2025-09-02T11:45:22","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=620"},"modified":"2025-09-02T11:45:24","modified_gmt":"2025-09-02T11:45:24","slug":"using-laravel-factories-and-seeders-for-test-data","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/using-laravel-factories-and-seeders-for-test-data\/","title":{"rendered":"Using Laravel Factories and Seeders for Test Data"},"content":{"rendered":"\n<p>Populating reliable test and demo data is essential for development speed, realistic QA, and repeatable CI. In this guide, you\u2019ll learn what <strong>factories<\/strong> are (and why they matter), how to build <strong>seeders<\/strong> that are safe and idempotent, how to scale with an <strong>extended folder structure<\/strong> for large projects, and how to tailor seeding for <strong>environments<\/strong> (local\/staging\/production) and even <strong>multi-tenant<\/strong> apps.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What Are Factories (and Why Use Them)?<\/strong><\/h2>\n\n\n\n<p><strong>Factories<\/strong> generate model instances with realistic attributes for testing, seeding, and demos. They keep your code DRY, encourage consistent data shapes, and make complex relationship graphs trivial to build. Instead of hand-writing arrays, you call factory builders that know how to produce valid models (with relationships, states, and sequences).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Basic Factory Definition<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/factories\/UserFactory.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Factories<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Eloquent<\/span>\\<span class=\"hljs-title\">Factories<\/span>\\<span class=\"hljs-title\">Factory<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Str<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UserFactory<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Factory<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $model = User::class;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">definition<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">array<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> &#91;\n            <span class=\"hljs-string\">'name'<\/span> =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;faker-&gt;name(),\n            <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;faker-&gt;unique()-&gt;safeEmail(),\n            <span class=\"hljs-string\">'email_verified_at'<\/span> =&gt; now(),\n            <span class=\"hljs-string\">'password'<\/span> =&gt; bcrypt(<span class=\"hljs-string\">'password'<\/span>), <span class=\"hljs-comment\">\/\/ don't use in production<\/span>\n            <span class=\"hljs-string\">'remember_token'<\/span> =&gt; Str::random(<span class=\"hljs-number\">10<\/span>),\n        ];\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This factory tells Laravel how to build a valid <code>User<\/code> with realistic defaults. Use it for tests (<code>User::factory()<\/code>) and also inside seeders to generate bulk data.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Factory States (Variants)<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/factories\/UserFactory.php (continued)<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">admin<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">static<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;state(fn () =&gt; &#91;\n        <span class=\"hljs-string\">'is_admin'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n    ]);\n}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">unverified<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">static<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;state(fn () =&gt; &#91;\n        <span class=\"hljs-string\">'email_verified_at'<\/span> =&gt; <span class=\"hljs-keyword\">null<\/span>,\n    ]);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>States<\/strong> let you quickly switch variants like admin users or unverified users: <code>User::factory()-&gt;admin()-&gt;create()<\/code>. Combine states freely.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Sequences (Patterned Data)<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">User::factory()\n    -&gt;count(<span class=\"hljs-number\">4<\/span>)\n    -&gt;sequence(\n        &#91;<span class=\"hljs-string\">'plan'<\/span> =&gt; <span class=\"hljs-string\">'free'<\/span>],\n        &#91;<span class=\"hljs-string\">'plan'<\/span> =&gt; <span class=\"hljs-string\">'pro'<\/span>],\n        &#91;<span class=\"hljs-string\">'plan'<\/span> =&gt; <span class=\"hljs-string\">'business'<\/span>],\n        &#91;<span class=\"hljs-string\">'plan'<\/span> =&gt; <span class=\"hljs-string\">'enterprise'<\/span>],\n    )\n    -&gt;create();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>Sequences<\/strong> rotate attribute sets per model, great for realistic mixes (pricing tiers, locales, statuses).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Relationships (for \/ has \/ hasAttached)<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Example: A Post belongs to a User and has many Tags via pivot<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Post<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tag<\/span>;\n\nPost::factory()\n    -&gt;for(User::factory()-&gt;admin(), <span class=\"hljs-string\">'author'<\/span>)\n    -&gt;hasAttached(Tag::factory()-&gt;count(<span class=\"hljs-number\">3<\/span>), &#91;<span class=\"hljs-string\">'weight'<\/span> =&gt; <span class=\"hljs-number\">1<\/span>]) <span class=\"hljs-comment\">\/\/ pivot fields<\/span>\n    -&gt;count(<span class=\"hljs-number\">10<\/span>)\n    -&gt;create();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>for()<\/code> assigns a parent relation; <code>has()<\/code>\/<code>hasAttached()<\/code> creates children (including pivot attributes). This builds correct graphs with one call.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Seeders: Repeatable, Idempotent Data<\/strong><\/h2>\n\n\n\n<p><strong>Seeders<\/strong> populate databases with base lookups, reference data, demo content, and admin accounts. Make them <em>idempotent<\/em> (safe to run multiple times) so deploys and CI stay reliable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>DatabaseSeeder as the Orchestrator<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/DatabaseSeeder.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeder<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">DatabaseSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;call(&#91;\n            LookupSeeder::class,\n            AdminUserSeeder::class,\n            DemoContentSeeder::class,\n        ]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The root seeder calls sub-seeders in a controlled order. Keep \u201cbase system\u201d seed separate from \u201cdemo\u201d seed for clarity.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Idempotent Patterns (firstOrCreate \/ upsert)<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/LookupSeeder.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Plan<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeder<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">LookupSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        Plan::upsert(&#91;\n            &#91;<span class=\"hljs-string\">'code'<\/span> =&gt; <span class=\"hljs-string\">'free'<\/span>, <span class=\"hljs-string\">'name'<\/span> =&gt; <span class=\"hljs-string\">'Free'<\/span>, <span class=\"hljs-string\">'price'<\/span> =&gt; <span class=\"hljs-number\">0<\/span>],\n            &#91;<span class=\"hljs-string\">'code'<\/span> =&gt; <span class=\"hljs-string\">'pro'<\/span>, <span class=\"hljs-string\">'name'<\/span> =&gt; <span class=\"hljs-string\">'Pro'<\/span>, <span class=\"hljs-string\">'price'<\/span> =&gt; <span class=\"hljs-number\">19<\/span>],\n        ], &#91;<span class=\"hljs-string\">'code'<\/span>], &#91;<span class=\"hljs-string\">'name'<\/span>,<span class=\"hljs-string\">'price'<\/span>]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>upsert<\/code> updates or inserts by a unique key; <code>firstOrCreate<\/code> is perfect for single rows. This keeps seeders re-runnable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Model::unguard + FK Handling (MySQL)<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Eloquent<\/span>\\<span class=\"hljs-title\">Model<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n\n<span class=\"hljs-comment\">\/\/ inside a seeder run():<\/span>\nModel::unguard();\nDB::statement(<span class=\"hljs-string\">'SET FOREIGN_KEY_CHECKS=0'<\/span>);\n<span class=\"hljs-comment\">\/\/ ... perform bulk inserts \/ truncations cautiously ...<\/span>\nDB::statement(<span class=\"hljs-string\">'SET FOREIGN_KEY_CHECKS=1'<\/span>);\nModel::reguard();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Temporarily unguard models for bulk inserts and use FK toggles carefully when resetting demo data. Avoid in production seed unless you know the implications.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Extended Folder Structure for Many Seeders<\/strong><\/h2>\n\n\n\n<p>Large apps benefit from grouping seeders by domain. Keep \u201ccore\u201d vs \u201cdemo\u201d clear, and isolate third-party integration seeds (e.g., roles\/permissions) for repeatability.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">database\/\n\u2514\u2500 seeders\/\n   \u251c\u2500 Core\/\n   \u2502  \u251c\u2500 Lookup\/\n   \u2502  \u2502  \u251c\u2500 CountriesSeeder.php\n   \u2502  \u2502  \u2514\u2500 PlansSeeder.php\n   \u2502  \u251c\u2500 Auth\/\n   \u2502  \u2502  \u251c\u2500 AdminUserSeeder.php\n   \u2502  \u2502  \u2514\u2500 RolesPermissionsSeeder.php\n   \u2502  \u2514\u2500 Content\/\n   \u2502     \u2514\u2500 SettingsSeeder.php\n   \u251c\u2500 Demo\/\n   \u2502  \u251c\u2500 UsersDemoSeeder.php\n   \u2502  \u251c\u2500 BlogDemoSeeder.php\n   \u2502  \u2514\u2500 OrdersDemoSeeder.php\n   \u2514\u2500 Tenants\/\n      \u251c\u2500 TenantBootstrapSeeder.php\n      \u2514\u2500 TenantDemoSeeder.php<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This structure scales: <code>Core<\/code> is minimal system data; <code>Demo<\/code> is optional; <code>Tenants<\/code> handles multi-tenant bootstrap. PSR-4 autoloading works for nested namespaces under <code>Database\\Seeders<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Namespacing &amp; Calling Sub-Seeders<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/DatabaseSeeder.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Lookup<\/span>\\<span class=\"hljs-title\">CountriesSeeder<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Lookup<\/span>\\<span class=\"hljs-title\">PlansSeeder<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Auth<\/span>\\<span class=\"hljs-title\">AdminUserSeeder<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Auth<\/span>\\<span class=\"hljs-title\">RolesPermissionsSeeder<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>\\<span class=\"hljs-title\">Demo<\/span>\\<span class=\"hljs-title\">BlogDemoSeeder<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">DatabaseSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;call(&#91;\n            CountriesSeeder::class,\n            PlansSeeder::class,\n            RolesPermissionsSeeder::class,\n            AdminUserSeeder::class,\n        ]);\n\n        <span class=\"hljs-keyword\">if<\/span> (app()-&gt;environment(&#91;<span class=\"hljs-string\">'local'<\/span>,<span class=\"hljs-string\">'staging'<\/span>])) {\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;call(&#91;BlogDemoSeeder::class]);\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Keep imports explicit. Use environment checks to include demo-only data locally while keeping production clean.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Environment &amp; Tenant-Aware Seeding<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Environment-Specific<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># run only demo seeds locally<\/span>\nphp artisan db:seed --class=Database\\\\Seeders\\\\Demo\\\\BlogDemoSeeder<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Use command-line control for targeted seeds. Inside <code>DatabaseSeeder<\/code>, conditionally call demo seeders with <code>app()-&gt;environment([...])<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Per-Tenant (Loop &amp; Switch)<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/Tenants\/TenantBootstrapSeeder.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tenant<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantBootstrapSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">foreach<\/span> (Tenant::cursor() <span class=\"hljs-keyword\">as<\/span> $tenant) {\n            <span class=\"hljs-comment\">\/\/ switch connection dynamically, then seed<\/span>\n            DB::setDefaultConnection($tenant-&gt;connection);\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;call(&#91;\n                TenantSchemaSeeder::class,\n                TenantAdminSeeder::class,\n            ]);\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Multi-tenant apps often seed per-tenant databases. Loop tenants, switch connection (or use a tenancy package helper), and call tenant-specific seeders.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Generating Bulk Data with Factories<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/Demo\/BlogDemoSeeder.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Post<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tag<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">BlogDemoSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        $users = User::factory()-&gt;count(<span class=\"hljs-number\">10<\/span>)-&gt;create();\n\n        $tags = Tag::factory()\n            -&gt;count(<span class=\"hljs-number\">8<\/span>)\n            -&gt;sequence(fn ($sequence) =&gt; &#91;<span class=\"hljs-string\">'weight'<\/span> =&gt; $sequence-&gt;index + <span class=\"hljs-number\">1<\/span>])\n            -&gt;create();\n\n        Post::factory()\n            -&gt;count(<span class=\"hljs-number\">50<\/span>)\n            -&gt;for($users-&gt;random(), <span class=\"hljs-string\">'author'<\/span>)\n            -&gt;hasAttached($tags-&gt;random(rand(<span class=\"hljs-number\">2<\/span>,<span class=\"hljs-number\">4<\/span>)))\n            -&gt;create();\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Factories make it trivial to create consistent, high-volume demo data with correct relations. Use <code>sequence<\/code> for predictable patterns and <code>random()<\/code> for variety.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Performance Tips<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Example: bulk inserts with minimal event noise<\/span>\nPost::withoutEvents(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Post::factory()-&gt;count(<span class=\"hljs-number\">2000<\/span>)-&gt;create();\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Wrap heavy factory runs with <code>withoutEvents<\/code> if event\/listener overhead is large. Consider chunking tasks, disabling observers, and using optimized indexes.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Using Factories Directly in Tests<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ tests\/Feature\/PostApiTest.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Tests<\/span>\\<span class=\"hljs-title\">TestCase<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Foundation<\/span>\\<span class=\"hljs-title\">Testing<\/span>\\<span class=\"hljs-title\">RefreshDatabase<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Post<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">PostApiTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">TestCase<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">RefreshDatabase<\/span>;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">test_index_requires_auth<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;getJson(<span class=\"hljs-string\">'\/api\/posts'<\/span>)-&gt;assertUnauthorized();\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">test_index_returns_posts<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        $user = User::factory()-&gt;create();\n        Post::factory()-&gt;count(<span class=\"hljs-number\">3<\/span>)-&gt;for($user, <span class=\"hljs-string\">'author'<\/span>)-&gt;create();\n\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;actingAs($user)-&gt;getJson(<span class=\"hljs-string\">'\/api\/posts'<\/span>)\n            -&gt;assertOk()\n            -&gt;assertJsonCount(<span class=\"hljs-number\">3<\/span>, <span class=\"hljs-string\">'data'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Factories shine in tests: realistic data, clearer intent, and isolated states per test using <code>RefreshDatabase<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Optional: Admin-Only UI to Run Demo Seeds<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\nRoute::post(<span class=\"hljs-string\">'\/admin\/run-demo-seed'<\/span>, &#91;AdminSeedController::class, <span class=\"hljs-string\">'run'<\/span>])\n    -&gt;middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'can:admin'<\/span>]);\n\n<span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/AdminSeedController.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Artisan<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AdminSeedController<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        Artisan::call(<span class=\"hljs-string\">'db:seed'<\/span>, &#91;\n            <span class=\"hljs-string\">'--class'<\/span> =&gt; <span class=\"hljs-string\">'Database\\\\Seeders\\\\Demo\\\\BlogDemoSeeder'<\/span>,\n            <span class=\"hljs-string\">'--force'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n        ]);\n\n        <span class=\"hljs-keyword\">return<\/span> back()-&gt;with(<span class=\"hljs-string\">'status'<\/span>,<span class=\"hljs-string\">'Demo data seeded.'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Expose a safe, admin-guarded endpoint to trigger demo seeds for staging or QA. Never allow anonymous access to seed endpoints.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>Factories produce realistic models with minimal code; seeders assemble them into coherent datasets for base lookups, demos, and tests. With a scalable folder structure, idempotent patterns (<code>upsert<\/code>\/<code>firstOrCreate<\/code>), and environment\/tenant awareness, your data pipeline stays fast, reliable, and CI-friendly.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What\u2019s Next<\/strong><\/h2>\n\n\n\n<p>Level up your testing and data quality with these:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/testing-laravel-applications-with-phpunit\">Testing Laravel Applications with PHPUnit<\/a><\/li>\n<li><a href=\"\/blog\/how-to-write-feature-tests-in-laravel-for-apis\">How to Write Feature Tests in Laravel for APIs<\/a><\/li>\n<li><a href=\"\/blog\/laravel-eloquent-relationships-explained-with-examples\">Laravel Eloquent Relationships Explained with Examples<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Populating reliable test and demo data is essential for development speed, realistic QA, and repeatable CI. In this guide, you\u2019ll learn what factories are (and why they matter), how to build seeders that are safe and idempotent, how to scale with an extended folder structure for large projects, and how to tailor seeding for environments [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":624,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[85,38,144,143,87],"class_list":["post-620","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-automation","tag-database","tag-factories","tag-seeders","tag-testing"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/620","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=620"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/620\/revisions"}],"predecessor-version":[{"id":623,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/620\/revisions\/623"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/624"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=620"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=620"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}