Shopware 6: Choosing Between Custom Fields and Entity Extensions for Product Data
Guide on choosing between Shopware 6 Custom Fields and Entity Extensions for product data. Learn when to use JSON-based attributes versus relational database extensions.
17 Aug 2025, 05:10 UTC

The Data Extension Dilemma
When adding new attributes to products in Shopware 6, developers often face a choice between Custom Fields and Entity Extensions. Choosing the wrong method leads to either a rigid database schema that is difficult to maintain or a performance bottleneck when filtering thousands of products in the storefront.
The core decision depends on whether you need simple metadata for display or structured data for complex querying and relational mapping.
Comparison of Extension Methods
| Feature | Custom Fields | Entity Extensions |
|---|---|---|
| Implementation | Admin Panel / API | PHP Code / Migrations |
| Storage | JSON Blob | Dedicated SQL Table |
| Complexity | Low (Key-Value) | High (Relational) |
| Query Performance | Slow for large filters | Fast (Indexed) |
| Type Safety | Limited (JSON) | Strong (DAL Definitions) |
| Storefront Access | Automatic | Requires Mapping/Loading |
When to Use Custom Fields
Custom Fields are best for non-relational data that primarily serves a descriptive purpose. Examples include a \"Material Composition\" text field or a \"Care Instructions\" toggle. Because they are stored as a JSON blob, they do not require database schema changes, making them ideal for rapid prototyping or client-managed attributes.
Constraint: Avoid Custom Fields if you intend to use the attribute as a primary filter in a high‑traffic category page. Searching within JSON blobs is significantly more resource‑intensive than querying an indexed column in a dedicated table.
When to Use Entity Extensions
Entity Extensions are necessary when you need to create a one-to-one or one-to-many relationship between a product and another entity. For example, if each product needs to link to a complex \"Technical Specification\" object with its own set of attributes and validations, an Entity Extension is the correct path.
Constraint: These require a database migration and a PHP Definition class. This increases the maintenance burden during Shopware platform updates, as you must ensure your custom schema remains compatible with core changes.
Implementing an Entity Extension
To implement an Entity Extension, you must register a new definition in the Data Abstraction Layer (DAL). This example assumes you are adding a ProductTechnicalDetail entity to the core ProductDefinition.
1. Create the Database Table
Run a migration from your plugin root to create the storage table. Ensure you include a foreign key to the product ID.
// In your Migration class execute() method
$connection->executeStatement(
'CREATE TABLE `custom_product_technical_detail` (
`id` BINARY(16) NOT NULL,
`product_id` BINARY(16) NOT NULL,
`detail_value` VARCHAR(255) NOT NULL,
`created_at` DATETIME(3) NOT NULL,
`updated_at` DATETIME(3) NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk.product_detail.product_id` FOREIGN KEY (`product_id`)
REFERENCES `product` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;'
);2. Register the Extension
Create an extension class that implements EntityExtensionInterface. This tells the DAL that the product entity now has a relationship with your new table.
// src/Extension/ProductExtension.php
class ProductExtension extends EntityExtension {
public function extendFields(FieldCollection $collection): void {
$collection->add(
new OneToManyAssociationField(
'technicalDetails',
ProductTechnicalDetailDefinition::class,
'product_id'
)
);
}
public function getDefinitionClass(): string {
return ProductDefinition::class;
}
}3. Register via services.xml
The extension must be tagged for the Shopware container to recognize it.
<service id="MyPlugin\\\\Extension\\\\ProductExtension">
<tag name="shopware.entity.extension"/>
</service>Validation and Verification
To verify the extension is working, perform a DAL search using the Criteria object. You must explicitly add the association to the criteria, as extensions are not loaded by default to save memory.
// Run within a Controller or Service
$criteria = new Criteria();
$criteria->addAssociation('technicalDetails');
$product = $this->productRepository->search($criteria, $context)->first();
// Check if the extension is populated
if ($product->hasExtension('technicalDetails')) {
// Success: Data is hydrated
}Rollback Procedure
If the Entity Extension causes hydration issues or memory spikes, you must:
- Remove the service registration from
services.xmlto stop the DAL from attempting to map the field. - Run a reverse migration to drop the
custom_product_technical_detailtable. - Clear the cache using
bin/console cache:clearto refresh the entity definitions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.