The FixtureBundle provides a flexible and organized way to load test data into your Shopware 6 application. It supports dependency management, priority-based execution, and group filtering.
composer require shopware/fixture-bundle:*
Create a class that implements FixtureInterface
and add the #[Fixture]
attribute:
<?php
declare(strict_types=1);
namespace App\Fixture;
use Shopware\FixtureBundle\FixtureInterface;
use Shopware\FixtureBundle\Attribute\Fixture;
#[Fixture]
class BasicFixture implements FixtureInterface
{
public function load(): void
{
// Your fixture logic here
echo "Loading basic fixture...\n";
}
}
Higher priority fixtures are executed first:
#[Fixture(priority: 100)]
class HighPriorityFixture implements FixtureInterface
{
public function load(): void
{
// This runs before fixtures with lower priority
}
}
Specify other fixtures that must be loaded before this one:
#[Fixture(
priority: 50,
dependsOn: [CategoryFixture::class]
)]
class ProductFixture implements FixtureInterface
{
public function load(): void
{
// CategoryFixture will always run before this
}
}
Organize fixtures into groups for selective loading:
#[Fixture(
groups: ['test-data', 'products']
)]
class ProductTestDataFixture implements FixtureInterface
{
public function load(): void
{
// This fixture belongs to both 'test-data' and 'products' groups
}
}
<?php
declare(strict_types=1);
namespace App\Fixture;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\FixtureBundle\FixtureInterface;
use Shopware\FixtureBundle\Attribute\Fixture;
#[Fixture(
priority: 100,
groups: ['catalog', 'test-data']
)]
class CategoryFixture implements FixtureInterface
{
public function __construct(
private readonly EntityRepository $categoryRepository,
private readonly Connection $connection
) {
}
public function load(): void
{
$categories = [
[
'id' => Uuid::randomHex(),
'name' => 'Electronics',
'active' => true,
],
[
'id' => Uuid::randomHex(),
'name' => 'Clothing',
'active' => true,
],
];
foreach ($categories as $category) {
$this->categoryRepository->create([$category], Context::createDefaultContext());
}
}
}
Load all fixtures:
bin/console fixture:load
Load only fixtures from a specific group:
bin/console fixture:load --group=test-data
View all available fixtures and their execution order:
bin/console fixture:list
List fixtures from a specific group:
bin/console fixture:list --group=test-data
Example output:
Available Fixtures
==================
------- -------------------- ---------- ----------------- ------------------
Order Class Priority Groups Depends On
------- -------------------- ---------- ----------------- ------------------
1 CategoryFixture 100 catalog, test- -
data
2 ManufacturerFixture 90 catalog -
3 ProductFixture 50 catalog, test- CategoryFixture,
data ManufacturerFixture
4 CustomerFixture 0 customers -
------- -------------------- ---------- ----------------- ------------------
[OK] Found 4 fixture(s).
The #[Fixture]
attribute accepts the following parameters:
- priority (int, default: 0): Higher values execute first
- dependsOn (array, default: []): Array of fixture class names that must run before this fixture
- groups (array, default: ['default']): Array of group names this fixture belongs to
Fixtures are executed in an order determined by:
- Dependencies: Fixtures with dependencies always run after their dependencies
- Priority: Among fixtures without dependency relationships, higher priority runs first
- Circular dependency detection: The system will throw an exception if circular dependencies are detected
Fixtures are automatically discovered and registered if they:
- Implement the
FixtureInterface
- Have the
#[Fixture]
attribute - Are registered as services (auto-configuration is enabled by default)
The FixtureBundle provides a convenient way to configure theme settings through fixtures using the ThemeFixtureLoader
and ThemeFixtureDefinition
classes.
<?php
declare(strict_types=1);
namespace App\Fixture;
use Shopware\FixtureBundle\Attribute\Fixture;
use Shopware\FixtureBundle\FixtureInterface;
use Shopware\FixtureBundle\Helper\Theme\ThemeFixtureDefinition;
use Shopware\FixtureBundle\Helper\Theme\ThemeFixtureLoader;
#[Fixture(groups: ['theme-config'])]
class ThemeFixture implements FixtureInterface
{
public function __construct(
private readonly ThemeFixtureLoader $themeFixtureLoader
) {
}
public function load(): void
{
$this->themeFixtureLoader->apply(
(new ThemeFixtureDefinition('Shopware default theme'))
->config('sw-color-brand-primary', '#ff6900')
->config('sw-border-radius-default', '8px')
->config('sw-font-family-base', '"Inter", sans-serif')
->config('sw-background-color', '#f8f9fa')
);
}
}
#[Fixture(groups: ['theme-config', 'branding'])]
class BrandingThemeFixture implements FixtureInterface
{
public function __construct(
private readonly ThemeFixtureLoader $themeFixtureLoader
) {
}
public function load(): void
{
// Configure main storefront theme
$this->themeFixtureLoader->apply(
(new ThemeFixtureDefinition('Shopware default theme'))
->config('sw-color-brand-primary', '#007bff')
->config('sw-color-brand-secondary', '#6c757d')
);
// Configure custom theme if available
try {
$this->themeFixtureLoader->apply(
(new ThemeFixtureDefinition('Custom Theme'))
->config('custom-header-color', '#ffffff')
->config('custom-footer-background', '#333333')
);
} catch (FixtureException $e) {
// Custom theme not available, skip
}
}
}
#[Fixture(groups: ['theme-config', 'branding'])]
class BrandingThemeFixture implements FixtureInterface
{
public function __construct(
private readonly ThemeFixtureLoader $themeFixtureLoader
) {
}
public function load(): void
{
// Will be uploaded just once and reused based on file content
$logo = $this->mediaHelper->upload(__DIR__ . '/shop.png', $this->mediaHelper->getDefaultFolder(ThemeDefinition::ENTITY_NAME)->getId());
// Configure main storefront theme
$this->themeFixtureLoader->apply(
(new ThemeFixtureDefinition('Shopware default theme'))
->config('sw-color-brand-primary', '#007bff')
->config('sw-color-brand-secondary', '#6c757d')
->config('sw-logo-desktop', $logo)
->config('sw-logo-tablet', $logo)
->config('sw-logo-mobile', $logo)
);
}
}
- Fluent Configuration: Chain multiple
->config()
calls for readability - Automatic Theme Discovery: Finds themes by name automatically
- Change Detection: Only updates and recompiles when configuration actually changes
- Error Handling: Throws
FixtureException::themeNotFound()
if theme doesn't exist - Automatic Recompilation: Theme is automatically recompiled after configuration changes
Common theme configuration fields include:
sw-color-brand-primary
- Primary brand colorsw-color-brand-secondary
- Secondary brand colorsw-border-radius-default
- Default border radiussw-font-family-base
- Base font familysw-background-color
- Background colorsw-logo-desktop
- Desktop logosw-logo-mobile
- Mobile logosw-logo-tablet
- Tablet logosw-logo-desktop-height
- Desktop logo heightsw-logo-mobile-height
- Mobile logo height
Note: Available fields depend on your theme's configuration schema defined in theme.json
The FixtureBundle provides helper classes to easily create and manage custom fields through fixtures using CustomFieldSetFixtureLoader
and related definition classes.
<?php
declare(strict_types=1);
namespace Acme\Fixture;
use Shopware\Core\System\CustomField\CustomFieldTypes;
use Shopware\FixtureBundle\Attribute\Fixture;
use Shopware\FixtureBundle\FixtureInterface;
use Shopware\FixtureBundle\Helper\CustomField\CustomFieldFixtureDefinition;
use Shopware\FixtureBundle\Helper\CustomField\CustomFieldSetFixtureDefinition;
use Shopware\FixtureBundle\Helper\CustomField\CustomFieldSetFixtureLoader;
#[Fixture]
class CustomFieldFixture implements FixtureInterface
{
public function __construct(
private readonly CustomFieldSetFixtureLoader $customFieldSetFixtureLoader
) {
}
public function load(): void
{
$this->customFieldSetFixtureLoader->apply(
(new CustomFieldSetFixtureDefinition('Product Specifications', 'product_specs'))
->relation('product')
->field(
(new CustomFieldFixtureDefinition('weight', CustomFieldTypes::FLOAT))
->label('en-GB', 'Weight (kg)')
->label('de-DE', 'Gewicht (kg)')
->placeholder('en-GB', 'Enter product weight')
->helpText('en-GB', 'Product weight in kilograms')
->position(10)
)
->field(
(new CustomFieldFixtureDefinition('dimensions', CustomFieldTypes::TEXT))
->label('en-GB', 'Dimensions')
->placeholder('en-GB', 'L x W x H')
->position(20)
)
->field(
(new CustomFieldFixtureDefinition('warranty_period', CustomFieldTypes::INT))
->label('en-GB', 'Warranty Period (months)')
->config(['min' => 0, 'max' => 120])
->position(30)
)
);
}
}
The FixtureBundle provides comprehensive customer management through fixtures using CustomerFixtureLoader
and CustomerFixtureDefinition
classes for creating test customers with addresses, custom fields, and relationships.
<?php
declare(strict_types=1);
namespace Acme\Fixture;
use Shopware\FixtureBundle\Attribute\Fixture;
use Shopware\FixtureBundle\FixtureInterface;
use Shopware\FixtureBundle\Helper\Customer\CustomerFixtureDefinition;
use Shopware\FixtureBundle\Helper\Customer\CustomerFixtureLoader;
#[Fixture]
class CustomerFixture implements FixtureInterface
{
public function __construct(
private readonly CustomerFixtureLoader $customerFixtureLoader
) {
}
public function load(): void
{
$this->customerFixtureLoader->apply(
(new CustomerFixtureDefinition('[email protected]'))
->firstName('John')
->lastName('Doe')
->salutation('mr')
->password('password123')
->company('ACME Corporation')
->department('IT Department')
);
}
}
#[Fixture(groups: ['customers', 'test-data'])]
class DetailedCustomerFixture implements FixtureInterface
{
public function __construct(
private readonly CustomerFixtureLoader $customerFixtureLoader
) {
}
public function load(): void
{
$this->customerFixtureLoader->apply(
(new CustomerFixtureDefinition('[email protected]'))
->firstName('Jane')
->lastName('Smith')
->salutation('mrs')
->title('Dr.')
->birthday('1990-05-15')
->company('Tech Solutions Ltd')
->department('Marketing')
->vatId('DE123456789')
->password('secure123')
->customerNumber('CUST-001')
->affiliateCode('PARTNER-123')
->campaignCode('SUMMER2024')
->active(true)
->guest(false)
->customFields([
'vip_level' => 'gold',
'newsletter_subscription' => true,
'preferred_contact' => 'email'
])
);
}
}
#[Fixture(groups: ['customers', 'addresses'])]
class CustomerWithAddressesFixture implements FixtureInterface
{
public function __construct(
private readonly CustomerFixtureLoader $customerFixtureLoader
) {
}
public function load(): void
{
$this->customerFixtureLoader->apply(
(new CustomerFixtureDefinition('[email protected]'))
->firstName('Max')
->lastName('Mustermann')
->salutation('mr')
->password('password')
->defaultBillingAddress([
'firstName' => 'Max',
'lastName' => 'Mustermann',
'street' => 'Musterstraße 123',
'zipcode' => '12345',
'city' => 'Musterstadt',
'country' => 'DEU',
'company' => 'Musterfirma GmbH',
'phoneNumber' => '+49 123 456789',
'salutation' => 'mr'
])
->defaultShippingAddress([
'firstName' => 'Max',
'lastName' => 'Mustermann',
'street' => 'Lieferadresse 456',
'zipcode' => '67890',
'city' => 'Lieferstadt',
'country' => 'DEU',
'additionalAddressLine1' => 'Building B',
'additionalAddressLine2' => '3rd Floor'
])
->addAddress('work', [
'firstName' => 'Max',
'lastName' => 'Mustermann',
'street' => 'Office Street 789',
'zipcode' => '11111',
'city' => 'Business City',
'country' => 'DEU',
'company' => 'Work Corporation'
])
);
}
}
#[Fixture(groups: ['customers', 'guest-orders'])]
class GuestCustomerFixture implements FixtureInterface
{
public function __construct(
private readonly CustomerFixtureLoader $customerFixtureLoader
) {
}
public function load(): void
{
$this->customerFixtureLoader->apply(
(new CustomerFixtureDefinition('[email protected]'))
->firstName('Guest')
->lastName('User')
->guest(true)
->active(false)
->defaultBillingAddress([
'firstName' => 'Guest',
'lastName' => 'User',
'street' => 'Guest Street 1',
'zipcode' => '99999',
'city' => 'Guest City',
'country' => 'DEU'
])
);
}
}
- Use meaningful names: Name your fixtures clearly to indicate what data they create
- Organize with groups: Use groups to categorize fixtures (e.g., 'test-data', 'demo-data', 'performance-test', 'theme-config', 'customers')
- Declare dependencies explicitly: Always declare dependencies to ensure correct execution order
- Keep fixtures focused: Each fixture should have a single responsibility
- Make fixtures idempotent: Fixtures should be able to run multiple times without errors
- Use dependency injection: Inject the services you need rather than accessing the container directly
- Handle theme errors gracefully: Use try-catch blocks when configuring optional themes
- Use email as unique identifier: Customer fixtures use email as the primary identifier for updates vs. creation