# runasroot/module-seeder

> Laravel-style database seeding for Magento 2

`composer require runasroot/module-seeder`

Canonical URL: https://packagento.com/runasroot/module-seeder

## At a glance

- **Vendor**: runasroot (https://packagento.com/runasroot.md)
- **Latest version**: 1.4.0 — released 2026-04-22
- **Pricing**: Free
- **Package type**: Magento 2 module
- **Status**: active, accepting new buyers

## Installation

Packagento is licence-gated, so even free packages need a licence on a project before Composer can resolve them.

1. **Sign in or create an account** at https://packagento.com/customer/account/.

2. **Add the package to your account.** Open https://packagento.com/runasroot/module-seeder and complete the free checkout. A licence is minted automatically.

3. **Create or pick a project, then activate the licence on it.**
   - Projects represent the Magento installs you deploy to. Manage them at https://packagento.com/projects/.
   - Activate the new licence on the project you'll deploy this package to. Activation is what generates the Composer credentials scoped to that project.

4. **Add the project credentials to your Magento codebase.**

   Grab the project's public + private key from https://packagento.com/projects/ (open the project, then its Credentials tab), and add them to `auth.json`:

   ```json
   {
     "http-basic": {
       "packagento.com": {
         "username": "ppk_live_...",
         "password": "psk_live_..."
       }
     }
   }
   ```

   Add the Packagento Composer repository to `composer.json`:

   ```json
   {
     "repositories": [
       { "type": "composer", "url": "https://packagento.com" }
     ]
   }
   ```

5. **Install and apply.**

   ```bash
   composer require runasroot/module-seeder:*
   bin/magento setup:upgrade
   bin/magento setup:di:compile
   bin/magento cache:flush
   ```

## What it does

Laravel-style database seeding for Magento 2

## README

[![CI](https://github.com/run-as-root/magento-2-seeder/actions/workflows/ci.yml/badge.svg)](https://github.com/run-as-root/magento-2-seeder/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Laravel-style database seeding for Magento 2 / Mage-OS. Define simple PHP / JSON / YAML files (or use the built-in Faker generators), run `bin/magento db:seed`, populate your dev environment with realistic products, categories, customers, orders, CMS content, reviews, cart rules, wishlists, and newsletter subscribers.

![db:seed --generate=order:10 --fresh](docs/demo/seed-orders.gif)

### Installation

```bash
composer require runasroot/module-seeder --dev
bin/magento module:enable RunAsRoot_Seeder
bin/magento setup:upgrade
```

### Quick Start

1. Scaffold a seeder: `bin/magento db:seed:make`
2. Run `bin/magento db:seed`

### Usage

```bash
## Run all seeders
bin/magento db:seed

## Run only specific types
bin/magento db:seed --only=customer,order

## Skip specific types
bin/magento db:seed --exclude=cms

## Wipe relevant data and re-seed
bin/magento db:seed --fresh

## Stop on first error
bin/magento db:seed --stop-on-error

## Combine flags
bin/magento db:seed --fresh --only=customer,product

## Show current DB counts of seeded entities
bin/magento db:seed:status
```

### Scaffolding

`db:seed:make` creates one or more seeder files for you — no need to memorize the format.

```bash
## Interactive — multi-select entity types, per-type counts, one file per type.
## Labels show cascade hints, e.g. "order — cascades: customer, product, category".
bin/magento db:seed:make

## Flag-driven (CI / scripts) — single type per invocation.
bin/magento db:seed:make --type=order --count=100 --format=php

## Overwrite an existing file
bin/magento db:seed:make --type=order --count=100 --force
```

Interactive mode pairs a multi-select (use space to toggle, enter to confirm) with a
per-type count prompt — picking `order`, `customer` writes both `OrderSeeder.<ext>`
and `CustomerSeeder.<ext>`. Shared prompts (locale, seed, format) apply to every
file written in the same run. If any target already exists you'll get a per-file
overwrite confirm; declined files are skipped and the rest continue.

Flag-driven mode stays single-type (`--type=X --count=N`); multi-type via flags
is not supported yet.

Available flags:

| Flag | Default | Notes |
|------|---------|-------|
| `--type` | — | required non-interactive |
| `--count` | — | required non-interactive |
| `--format` | `php` | `php` / `json` / `yaml` |
| `--name` | `{Type}Seeder` | file name without extension |
| `--locale` | `en_US` | Faker locale |
| `--seed` | random | Faker seed for deterministic output |
| `--force` | `false` | overwrite existing file |

### Seeder Formats

Seeder files live in `dev/seeders/` and must end in `Seeder.<ext>`. Three formats are supported:

| Format | Extension         | Use when                                            |
|--------|-------------------|-----------------------------------------------------|
| PHP    | `.php`            | Array or class with loops / Faker / conditionals    |
| JSON   | `.json`           | Machine-generated fixtures or cross-language tools  |
| YAML   | `.yaml` / `.yml`  | Human-readable fixtures                             |

All three share the same payload shape: `type`, `data` (or `count`), and optional `order`/`locale`/`seed`.

#### Array-Based (PHP)

Create a PHP file ending in `Seeder.php` that returns an array with `type` and `data`:

```php
<?php
// dev/seeders/CustomerSeeder.php
return [
    'type' => 'customer',
    'data' => [
        ['email' => 'john@test.com', 'firstname' => 'John', 'lastname' => 'Doe', 'password' => 'Test1234!'],
        ['email' => 'jane@test.com', 'firstname' => 'Jane', 'lastname' => 'Doe', 'password' => 'Test1234!'],
    ],
];
```

#### JSON

```json
// dev/seeders/CustomerSeeder.json
{
    "type": "customer",
    "data": [
        {"email": "john@test.com", "firstname": "John", "lastname": "Doe", "password": "Test1234!"},
        {"email": "jane@test.com", "firstname": "Jane", "lastname": "Doe", "password": "Test1234!"}
    ]
}
```

#### YAML

```yaml
## dev/seeders/CustomerSeeder.yaml
type: customer
data:
  - email: john@test.com
    firstname: John
    lastname: Doe
    password: Test1234!
  - email: jane@test.com
    firstname: Jane
    lastname: Doe
    password: Test1234!
```

Invalid JSON/YAML files are logged to `var/log/` and skipped; the rest of the run continues.

#### Class-Based (fluent, recommended)

For complex scenarios, extend `RunAsRoot\Seeder\Seeder` and use the fluent builder:

```php
<?php
// dev/seeders/MassOrderSeeder.php
use RunAsRoot\Seeder\Seeder;

class MassOrderSeeder extends Seeder
{
    public function getType(): string { return 'order'; }
    public function getOrder(): int { return 40; }

    public function run(): void
    {
        $this->orders()
            ->count(50)
            ->with(['items' => [['sku' => 'TSHIRT-001', 'qty' => 2]]])
            ->create();
    }
}
```

Available builder entry points: `customers()`, `products()`, `orders()`, `categories()`, `cms()`, plus `seed('custom_type')` for types registered via `di.xml`.

Builder methods:

| Method | Purpose |
|---|---|
| `->count(int $n)` | How many to create |
| `->with(array $data)` | Static overrides merged into each iteration (shallow replace) |
| `->using(callable $fn)` | Per-iteration callback: `fn(int $i, Faker\Generator $faker): array` |
| `->subtype(string $s)` | Force subtype (e.g. `'bundle'` for products, `'complete'` for orders) |
| `->create()` | Executes and returns `int[]` of created ids |

Precedence (most specific wins): `using()` > `with()` > generator Faker defaults.

If your subclass needs its own dependencies, override the constructor and call `parent::__construct(...)`:

_(README truncated for .md surface. Full README on https://packagento.com/runasroot/module-seeder.)_

## Changelog

All notable changes to `runasroot/module-seeder` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### [1.4.0] - 2026-04-22

#### Added

- `CommerceProvider` — a new `Faker\Provider\Base` subclass registered on every `Faker\Generator` that `FakerFactory` hands out. Exposes five methods mirroring the `commerce` module from [@faker-js/faker](https://github.com/faker-js/faker) (MIT): `$faker->productName()`, `->productAdjective()`, `->productMaterial()`, `->product()`, `->productDepartment()`. Eliminates lorem-ipsum product names in seeded catalogs — `Dolor Sit Amet` becomes `Handcrafted Rubber Pizza`.
- `CommerceLocaleInterface` + `CommerceProviderFactory` — locale-aware wiring with silent English fallback. `en_US` ships with 27 adjectives, 15 materials, 24 products, and 22 departments ported verbatim from @faker-js/faker's `en` commerce locale at upstream commit `9e2c0e391b436f56ff54ad89d02efa9982406389`. Unknown locales (incl. `de_DE` in v1) fall back to the English wordlists without warning — mirrors how Faker itself handles unmapped locales.
- `src/Faker/Provider/Data/Commerce/README.md` — refresh + locale-extension guide. Explains how to re-sync wordlists from FakerJS (manual, ~5 min) and how to add new locale data classes.
- `NOTICE` at repo root — MIT attribution for the ported wordlists, pinned to the source commit.
- README section documenting the new `$faker->product*()` methods with a one-line example per method and a note on locale scope.

#### Changed

- `ProductDataGenerator::generate()` now uses `$faker->productName()` in place of `ucwords($faker->words(2-4, true))`. Product names are now three-word commerce-style strings drawn from curated upstream wordlists. Name-field assertions in user seeder tests that relied on the lorem-word shape will need updating (shape assertions like "non-empty string" keep working unchanged).
- `CategoryDataGenerator::generate()` now uses `$faker->productDepartment()` in place of the hardcoded 20-entry `COMMERCE_CATEGORIES` const + random-lorem-word suffix. Names like `Electronics labore` become clean `Books` / `Jewelery` / `Automotive`. The `COMMERCE_CATEGORIES` private const was removed.

#### Fixed

- n/a — this release is purely additive in user-facing behavior.

#### Installation

```bash
composer require runasroot/module-seeder:^1.4
bin/magento setup:upgrade
```

Fully backward compatible with 1.3.x — no breaking public API changes. `FakerFactory::__construct()` gained an optional `?CommerceProviderFactory` argument; the factory self-constructs a default when none is passed, so any caller doing `new FakerFactory()` keeps working unchanged. No new composer requires.

#### Contributors

- @DavidLambauer — entire release

### [1.3.0] - 2026-04-21

#### Added

_(Changelog truncated for .md surface. Full history on https://packagento.com/runasroot/module-seeder.)_

## Recent Versions

| Version | Released |
|---|---|
| 1.4.0 | 2026-04-22 |
| 1.3.0 | 2026-04-21 |
| 1.2.1 | 2026-04-20 |
| 1.2.0 | 2026-04-20 |
| 1.1.0 | 2026-04-20 |
| 1.0.0 | 2026-04-19 |

## Dependencies

### Require

| Package | Constraint |
|---|---|
| fakerphp/faker | ^1.23 |
| laravel/prompts | ^0.3 |
| magento/framework | * |
| magento/module-bundle | * |
| magento/module-catalog | * |
| magento/module-catalog-inventory | * |
| magento/module-checkout | * |
| magento/module-cms | * |
| magento/module-configurable-product | * |
| magento/module-customer | * |
| magento/module-downloadable | * |
| magento/module-grouped-product | * |
| magento/module-newsletter | * |
| magento/module-quote | * |
| magento/module-sales | * |
| magento/module-sales-rule | * |
| magento/module-wishlist | * |
| php | ^8.1 |
| symfony/yaml | ^6.0 \|\| ^7.0 |

### Require (dev)

| Package | Constraint |
|---|---|
| magento/magento-coding-standard | * |
| mockery/mockery | ^1.6 |
| phpstan/phpstan | ^1.11 |
| phpunit/phpunit | ^10.0 |
| psr/log | ^3.0 |
| squizlabs/php_codesniffer | ^3.10 |
| symfony/console | ^6.0 \|\| ^7.0 |

## Quality

Latest release (1.4.0) fails the Packagento QA pipeline. Verdicts below are per-cell (Magento line × PHP version) for the matrixed tools, and run-once for the static / security tiers.


### Compatibility

Each Magento line is installed on its supported PHP versions, then the module is built (DI compile + static-content deploy). Cells show passed / failed / untested; staircase gaps render as `–`.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | Pass | Pass | – | – |
| 2.4.8 | – | Pass | Pass | – |
| 2.4.9 | – | – | not tested | not tested |


### Code Quality

Advisory checks against the module's source. Never affect the Compatibility verdict — a phpcs finding can't make a module incompatible.

#### Static Analysis

Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Fail | 851 | 300 errors, 551 warnings (ruleset: Magento2) — 93 auto-fixable with phpcbf |
| PHPMD | Warning | 43 | 43 rule violations (MissingImport:29, CyclomaticComplexity:4, NPathComplexity:3, ExcessiveMethodLength:2, EmptyCatchBlock:2) |
| Cpd | Warning | 9 | 9 duplicated chunks spanning 188 total lines (min-lines=5, min-tokens=70) |
| Composer validate | Info | 15 | valid; 15 advisory notes (composer validate --strict) |

#### PHPStan

Type-checks the module against a real Magento install. Re-runs per Magento + PHP version because resolvable symbols differ between releases.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | 39 | 39 | – | – |
| 2.4.8 | – | 39 | 39 | – |
| 2.4.9 | – | – | 39 | 39 |


### Tests

Unit and integration suites run per Magento + PHP cell. Test failures speak to the module's behaviour, not its compatibility with a line, so they're reported here separately.

#### Unit Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | 54 | 54 | – | – |
| 2.4.8 | – | 54 | 54 | – |
| 2.4.9 | – | – | not tested | Error |

#### Integration Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | Error | Error | – | – |
| 2.4.8 | – | Error | Error | – |
| 2.4.9 | – | – | not tested | Error |


### Security

Dependency-advisory audit (composer audit) plus a source malware scan. A malware detection fails the version outright.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| Composer audit | Pass | 0 |  |
| Malware scan | Pass | 0 |  |

## Licence and pricing

Free. A licence is still minted on checkout and bound to your project for Composer access — no payment step.

Refundable within 14 days of first purchase via https://packagento.com/account/refunds/.

## Install via Claude Code or any MCP client

The Packagento MCP server can run the licence + project + Composer steps above in one tool call:

```
purchase_and_install_packages(
  composer_names=["runasroot/module-seeder"],
  project_id="proj_xxx"
)
```

This handles cart, checkout, licence minting, project activation, and writes auth.json credentials. Connect a client with `claude mcp add packagento https://mcp.packagento.com`. Full setup at https://packagento.com/docs/mcp-setup.

## Vendor

runasroot is a Magento 2 vendor on Packagento. See https://packagento.com/runasroot.md for their full catalogue.

