PHPackages                             basilicom/pimcore-fixtures - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. basilicom/pimcore-fixtures

ActivePimcore-bundle[Testing &amp; Quality](/categories/testing)

basilicom/pimcore-fixtures
==========================

Export Pimcore content to version-controlled YAML and load it into any environment. Follows cross-domain references automatically — objects, documents, assets, and system config in one command.

v2026.8.0(1mo ago)1582↓70.2%MITPHPPHP ^8.3CI passing

Since May 19Pushed 1mo agoCompare

[ Source](https://github.com/basilicom/pimcore-fixtures)[ Packagist](https://packagist.org/packages/basilicom/pimcore-fixtures)[ Docs](https://github.com/basilicom/pimcore-fixtures)[ RSS](/packages/basilicom-pimcore-fixtures/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (10)Dependencies (21)Versions (15)Used By (0)

Pimcore YML Fixtures
====================

[](#pimcore-yml-fixtures)

**Stop rebuilding test content by hand. Put your Pimcore content in Git and recreate it anywhere with one command.**

Every Pimcore team has lost a day to it: a new developer needs a working dataset, a CI run needs known content, a customer bug only reproduces against *their* data. The usual answer is a stale, oversized database dump full of someone else's test garbage. This bundle replaces that with plain YAML you can commit, diff, and review.

Point it at a folder in your Pimcore tree and it writes everything it finds to YAML — data objects, documents, assets, plus the system config they depend on. Commit those files, and any teammate or environment recreates the exact same Pimcore state with `bin/console basilicom:pimcore:fixtures:load`.

**What sets it apart:** it follows *cross-domain references* automatically. Export `/products` and the categories, hero images, and landing pages those products point to come along too — including parent folders, so every path resolves on the other side. Most fixture tools dump objects and stop. This one brings the whole graph.

Because fixtures are readable YAML with `@key` references, a content change shows up as a reviewable Git diff — and `--diff` previews exactly what a load would change before you run it.

Typical use cases:

- **Onboard developers in minutes** — clone, `load`, done. No begging a colleague for a DB dump.
- **Deterministic CI** — `reset --force && load` gives every test run identical, known content.
- **Reproducible bug reports** — snapshot the affected subtree (refs followed) and debug locally against the real data.
- **Ship demo data with your product** — bundle a curated dataset; the installer loads it and the demo looks great instantly.
- **Config as code** — keep sites, roles, glossary, units, and classification stores in version control and promote them env-to-env.

Works with Pimcore 12 / PHP 8.3 / Symfony 7.

---

Installation
------------

[](#installation)

```
composer require --dev basilicom/pimcore-fixtures
```

Register the bundle in `config/bundles.php`:

```
return [
    // ...
    Basilicom\PimcoreFixtures\PimcoreFixturesBundle::class => ['dev' => true],
    // ...
];
```

Optional config in `config/packages/basilicom_pimcore_fixtures.yaml`:

```
basilicom_pimcore_fixtures:
  path: '%kernel.project_dir%/fixtures'   # where fixture files live
  format: yaml                             # fixture file format: yaml (default) or json
  ignored_fields: []                       # DataObject field names to skip
  ignored_classes: []                      # DataObject classes to skip
  ignored_paths: []                        # Pimcore paths to skip
  ignored_properties: []                   # Pimcore property names to skip on documents/assets/dataObjects
  ignored_website_settings: []             # Website setting names to skip
```

Defaults are sensible — `path` falls back to `var/bundles/PimcoreFixtures`. Empty values (`null`, `''`, `[]`) and inherited values are skipped during generation to keep files clean.

`format` controls the serialization of every fixture file the bundle writes *and* reads — data objects, documents, asset metadata, and all `pimcore/*` config fixtures. With `yaml` (the default), loading accepts both `.yaml` and `.yml`; with `json`, only `.json` files are read.

> **Switching formats does not convert existing fixtures.** After changing `format`, files in the old format become invisible to both export-merge and `fixtures:load`. Convert them manually (the data structures are identical) or regenerate from a seeded system, and remove the old files.

`ignored_properties` matches on `Property::getName()` and applies wherever Pimcore properties are exported (document YAML, asset `_metadata.yaml`, and dataObject property holders). `ignored_website_settings` matches on `WebsiteSetting::getName()` and filters entries before they are written to `pimcore/website_settings.yaml`.

---

How it works
------------

[](#how-it-works)

The bundle has two sides:

- **Generate** — read live Pimcore content and write YAML fixture files.
- **Load** — read those YAML files and recreate the content in another Pimcore instance.

Loading is safe to re-run: by default an element that already exists at the same path is left untouched and only missing elements are created. Pass `--overwrite` to also update existing elements (see *Loading fixtures* below). Either way, content is never duplicated.

Below is a tour of the main commands. Run any of them with `--help` to see all options.

---

What gets exported
------------------

[](#what-gets-exported)

### Everything at once

[](#everything-at-once)

```
bin/console basilicom:pimcore:fixtures:generate
```

The umbrella command: exports the complete content set in one run — all data objects, documents, and asset binaries from their tree roots, plus every system-config fixture (sites, units, glossary, website settings, roles, classification store, translations). Use `--levels` to cap tree depth, `--skip-translations` to leave translations out, and `--force` to overwrite instead of merge.

For scoped or interactive exports, use the dedicated per-domain commands below.

### Data objects

[](#data-objects)

```
bin/console basilicom:pimcore:fixtures:generate-objects
```

Without arguments it opens an interactive picker (filter, ↑/↓, space, enter) to choose one or more folders from your data object tree. Pass `--folder=/products` to skip the picker, or `--all` to export everything.

By default it also follows **cross-domain references**: if a Product points to a Category, a hero image, and a landing page, all four are pulled into the same export — including parent folders so paths resolve cleanly on load. Disable with `--no-follow-refs`.

Re-running merges new entries into existing fixture files. Pass `--force` to overwrite from scratch.

### Documents

[](#documents)

```
bin/console basilicom:pimcore:fixtures:generate-documents
```

Same flags as `generate-objects`. Exports pages, snippets, links, emails, folders, and hardlinks to `{path}/documents/`. Includes title, description, pretty URL, controller, template, editables, link targets, properties, and the original sibling `index` so loaded documents keep their Pimcore tree order. Reference following pulls in any data objects and assets the documents link to.

Hardlinks are exported with their `source` (as an `@key` reference to the source document) and the `childrenFromSource` / `propertiesFromSource` flags. The source document is pulled into the export via reference following, so the link resolves on load.

### Assets

[](#assets)

```
bin/console basilicom:pimcore:fixtures:generate-assets
```

Copies asset binaries into `{path}/assets/`, mirroring the Pimcore asset tree. On load, files are restored to their original Pimcore paths. Same flag set as the other generators (interactive picker, `--folder`, `--all`, `--levels`, `--force`, …).

You can also drop replacement files into `fixtures/assets/` manually — merge mode leaves existing binaries untouched.

Per-asset state that can't be expressed by the binary itself is written to `{path}/assets/_metadata.yaml`: own (non-inherited) Pimcore properties *and* Pimcore asset metadata items (the "Metadata" tab — `name`/`type`/`data`/`language`, with element-typed values stored as paths). The asset seeder reads this sidecar after creating the binaries and applies both.

### System configuration

[](#system-configuration)

A few Pimcore-wide settings travel with your fixtures, written as single files under `{path}/pimcore/`:

FileContains`sites.yaml`Sites and their root documents`website_settings.yaml`Website settings (with element references resolved by path)`glossary.yaml`Glossary entries`units.yaml`QuantityValue units`classification_store.yaml`Stores, groups, keys, collections (the *config*, not field values)`roles.yaml`Roles, role folders, permissions, workspaces`shared_translations.yaml`Shared translations (default `messages` domain)`admin_translations.yaml`Admin UI translations (`admin` domain)All of them are emitted when you run `generate`. Classification stores, roles, and translations also have their own dedicated commands if you want to regenerate just one:

```
bin/console basilicom:pimcore:fixtures:generate-classification-store
bin/console basilicom:pimcore:fixtures:generate-roles
bin/console basilicom:pimcore:fixtures:generate-translations
```

**Translation file format** — each top-level key is a translation key, each value is a `locale → text` map:

```
my.label.headline:
  de: 'Überschrift'
  en: 'Headline'
my.label.subtitle:
  de: 'Untertitel'
  en: 'Subtitle'
```

On load, missing keys are created; existing keys are left untouched by default and only updated with `--overwrite`. Locales not configured in Pimcore are skipped with a warning. Pass `--skip-translations` to `generate` or `load` to opt out entirely.

> File-based config that already lives in `var/config/` — static routes, predefined properties, document types — isn't fixturized. Commit those files directly.

---

Loading fixtures
----------------

[](#loading-fixtures)

```
bin/console basilicom:pimcore:fixtures:load
```

Loads everything in the fixtures folder back into Pimcore in the right order: units and classification config first, then assets, document shells, data objects (in two passes so circular relations work), document content, sites, glossary, translations, website settings, and finally roles.

Useful options:

OptionEffect`--dry-run`Preview without writing`--diff`Preview *what would change* in the live data object tree, field by field, without writing. Implies `--dry-run`.`--overwrite`Update elements that already exist at the same path. Without it, existing elements are skipped and only missing ones are created.`--force`Required to write in the `prod` environment. No effect elsewhere.`--omit-validation`Skip mandatory-field validation when saving objects`--skip-translations`Don't load shared/admin translation fixtures**Existing elements — skip vs. overwrite.** Data objects, documents and assets are matched by their Pimcore path (ids differ between environments) through a single `ExistingElementResolver`; config fixtures are matched by their natural key. The behaviour is one consistent axis across all of them:

- **default (no `--overwrite`)** — an element that already exists at the target path is left completely untouched; only missing elements are created. The safe default for promoting a subtree into a populated environment.
- **`--overwrite`** — the fixture's fields are written onto the existing element. Fields the fixture does *not* mention keep their live values (generation omits empty/inherited fields, so "unmentioned" means "the fixture has nothing to say about it"). For assets this also replaces the binary.

Neither mode clears fields the fixture omits. A *clean* snapshot — wiping fields that exist only in the target — is `reset --force` followed by `load`, not a load mode.

`--overwrite` applies to **every** domain — data objects, documents, assets, and the system-config fixtures (sites, website settings, glossary, units, classification-store config, roles, translations). The config fixtures match existing entries by their natural key (translation key, unit id, role name, site domain, store/group/key name, …): in the default mode an existing entry is left untouched, with `--overwrite` it is updated.

> One exception by necessity: the classification-store config still **descends into** an existing store to create any *missing* groups/keys even in the default mode (object hydration needs those to exist) — it just doesn't modify the ones already there. Existing groups/keys are only updated with `--overwrite`.

**Prod safety net.** In the `prod` environment a writing load refuses to run unless `--force` is passed. Read-only previews (`--dry-run`, `--diff`) are always allowed.

**`--diff`** loads nothing — it compares each fixture data object against the live object at the same path and prints the differences:

```
~ /products/example-shoe  (Product)
    name: 'Old Shoe' → 'New Shoe'
    price: 49.9 → 39.9
+ /products/brand-new      (Product) [new]

Diff: 1 new, 1 changed.

```

Use it to sanity-check a load against a populated environment before committing to it. Field-level detail covers data objects; documents, assets, and system config run as a dry run under `--diff`.

> `--diff` shows the *overwrite* view (what `--overwrite` would change). In the default mode existing elements are skipped, so a plain `load` would not apply those changes — only create the entries marked `[new]`.

**Schema drift:** if a fixture references a data object field that no longer exists on the current class definition (renamed or removed since the fixture was generated), the load no longer aborts. The field is skipped and surfaced as a warning in the end-of-run summary, so a stale fixture degrades gracefully instead of crashing the whole import.

---

Resetting
---------

[](#resetting)

```
bin/console basilicom:pimcore:fixtures:reset --force
```

Truncates Pimcore content tables before a fresh load. The `--force` flag is mandatory in every environment — it is the deliberate gate between you and an empty database, on prod as much as on dev.

Scope it with `--types` (`objects`, `documents`, `assets`), `--classes`, `--exclude`, or `--drop-folder`. Some shared tables (`notes`, `edit_lock`, `search_backend_data`, `recyclebin`) are always truncated.

---

Fixture file format
-------------------

[](#fixture-file-format)

Fixtures are plain YAML (or JSON, see the `format` config option) keyed by class and fixture id, with `@key` references between entries. The structure is identical in both formats — JSON merely escapes the backslashes in class-name keys (`"\\App\\Model\\DataObject\\Product"`).

**Data object example:**

```
\App\Model\DataObject\Product:
  001_product_example_abc123:
    key: example-product
    parentId: 1
    published: true
    name: 'Example product'
    category: '@002_category_shoes_def456'   # cross-fixture reference
```

**Document example:**

```
\Pimcore\Model\Document\Page:
  003_page_about_xyz789:
    key: about-us
    parent: '@001_folder_content_abc123'
    title: 'About Us'
    template: '@AppBundle/Default/default.html.twig'
    properties:
      - { name: nav_title, type: text, data: About, inheritable: false }
```

**Classification store field values** on a data object are nested by `groupName → keyName → language`:

```
classificationAttributes:
  technicalSpecs:
    weight:
      default: '2.5 kg'
  marketingData:
    headline:
      de: Produktüberschrift
      en: Product headline
```

---

Extending
---------

[](#extending)

You can teach the bundle about new Pimcore field types or custom load behaviour without forking it.

- **Custom field type** — add a transformer in `src/Generation/DataTransformer/` for export, and a hydrator implementing `ChainedPropertyHydratorInterface` for load. Tag the hydrator service with `basilicom_pimcore_fixtures.property_hydrator` and a priority.
- **Custom property hydrator** — same tag, used as a fallback when none of the built-in hydrators match.
- **Pre/post processors** — extend `AbstractProcessor` and tag with `basilicom_pimcore_fixtures.pre_processor` or `basilicom_pimcore_fixtures.post_processor` to run code around each object save.
- **Custom fixture format** — implement `FixtureFormatInterface` and tag the service with `basilicom_pimcore_fixtures.fixture_format`. Note that the `format` config option validates against the built-in names (`yaml`, `json`), so a custom format also needs that enum extended via a PR.

See [AGENTS.md](./AGENTS.md) for architectural details.

---

Contributing
------------

[](#contributing)

You don't need a local PHP or Composer install — everything runs in Docker via `make`.

```
git clone git@github.com:basilicom/pimcore-fixtures.git
cd pimcore-fixtures
make setup
```

Common tasks:

CommandPurpose`make lint`PHP-CS-Fixer + PHPStan`make lint-php-fix`Auto-fix style issues`make test-unit`Run PHPUnit`make help`List everythingRun `make lint && make test-unit` before opening a PR. Architecture and conventions are documented in [AGENTS.md](./AGENTS.md).

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance92

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 90.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

11

Last Release

45d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/afb6145ee5c0c6af7662a537e76ee9c679d30e7e6217636d72576ea5ccd8b002?d=identicon)[AlexanderHeidrich](/maintainers/AlexanderHeidrich)

---

Top Contributors

[![AlexanderHeidrich](https://avatars.githubusercontent.com/u/9350895?v=4)](https://github.com/AlexanderHeidrich "AlexanderHeidrich (19 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")[![kerstinbasilicom](https://avatars.githubusercontent.com/u/38213550?v=4)](https://github.com/kerstinbasilicom "kerstinbasilicom (1 commits)")

---

Tags

testingfixturesyamlseedingcipimcorepimcore-bundleDemo dataseed-datacontent-migration

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/basilicom-pimcore-fixtures/health.svg)

```
[![Health](https://phpackages.com/badges/basilicom-pimcore-fixtures/health.svg)](https://phpackages.com/packages/basilicom-pimcore-fixtures)
```

###  Alternatives

[liip/test-fixtures-bundle

This bundles enables efficient loading of Doctrine fixtures in functional test-cases for Symfony applications

1799.0M52](/packages/liip-test-fixtures-bundle)[rregeer/phpunit-coverage-check

Check the code coverage using the clover report of phpunit

636.5M219](/packages/rregeer-phpunit-coverage-check)[pimcore/skeleton

127200.6k](/packages/pimcore-skeleton)[moodlehq/moodle-plugin-ci

Helps running Moodle plugins analysis checks and tests under various CI environments.

642.9M](/packages/moodlehq-moodle-plugin-ci)[acquia/orca

A tool for testing a company's software packages together in the context of a realistic, functioning, best practices Drupal build

30919.1k](/packages/acquia-orca)[friendsofcake/fixturize

CakePHP Fixture classes to help increase productivity or performance

24520.3k1](/packages/friendsofcake-fixturize)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
