Introduction
Shopware 6.7 introduces significant improvements to the platform's extensibility capabilities, particularly when it comes to customizing core entities like products. One of the most common requirements for developers is the ability to add custom fields to existing entities to extend their functionality without modifying core code. This blog post will guide you through the complete process of adding a custom field to the Product entity in Shopware 6.7, covering both the database schema changes and the necessary configuration files.
Understanding Custom Fields in Shopware 6.7
Before diving into implementation details, it's crucial to understand how custom fields work in Shopware 6.7. Custom fields are a powerful feature that allows developers to extend entities with additional data without altering the core database structure. They're stored in a flexible JSON format and can be managed through the administration panel, making them accessible to both developers and content editors.
In Shopware 6.7, custom fields have been enhanced with better performance optimizations and more intuitive management interfaces. The platform now provides dedicated services and methods for handling custom field collections, making integration much smoother than in previous versions.
Prerequisites
Before starting this implementation, ensure you have:
- A working Shopware 6.7 installation
- Development environment with proper permissions
- Basic understanding of Symfony and Shopware architecture
- Access to the database and administration panel
Step 1: Create the Custom Field Definition
The first step in adding a custom field to the Product entity is to define the field itself. This involves creating a new service that registers your custom field with the system.
<?php declare(strict_types=1);
namespace YourPlugin\CustomField;
use Shopware\Core\Framework\DataAbstractionLayer\CustomField\CustomFieldDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\CustomField\CustomFieldTypes;
use Shopware\Core\Framework\DataAbstractionLayer\Field\BoolField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\FloatField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\IntField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\LongTextField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\TextField;
class ProductCustomFieldDefinition
{
public static function getCustomFields(): array
{
return [
'your_plugin_product' => [
'type' => CustomFieldTypes::TEXT,
'name' => 'your_plugin_product_field',
'label' => [
'de-DE' => 'Your Plugin Product Field',
'en-GB' => 'Your Plugin Product Field'
],
'config' => [
'componentName' => 'sw-field',
'type' => 'text',
'placeholder' => 'Enter custom text here'
]
]
];
}
}
Step 2: Create the Plugin Configuration
Next, you need to register your custom field definition in your plugin's configuration. Create a config.xml file in your plugin's root directory:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/platform/master/src/Core/System/SystemConfig/Schema/config.xsd">
<elements>
<element name="your_plugin_product_field">
<label lang="de-DE">Your Plugin Product Field</label>
<label lang="en-GB">Your Plugin Product Field</label>
<type>text</type>
<helpText lang="de-DE">Beschreibung des benutzerdefinierten Feldes</helpText>
<helpText lang="en-GB">Description of the custom field</helpText>
</element>
</elements>
</config>
Step 3: Implement the Custom Field Service
Create a service that handles the registration and management of your custom field. This service will integrate with Shopware's dependency injection container:
<?php declare(strict_types=1);
namespace YourPlugin\Service;
use Doctrine\DBAL\Connection;
use Psr\Log\LoggerInterface;
use Shopware\Core\Framework\DataAbstractionLayer\CustomField\CustomFieldDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\CustomField\CustomFieldRepository;
use Shopware\Core\Framework\DataAbstractionLayer\CustomField\CustomFieldTypes;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ProductCustomFieldService
{
private CustomFieldRepository $customFieldRepository;
private Connection $connection;
private LoggerInterface $logger;
public function __construct(
CustomFieldRepository $customFieldRepository,
Connection $connection,
LoggerInterface $logger
) {
$this->customFieldRepository = $customFieldRepository;
$this->connection = $connection;
$this->logger = $logger;
}
public function installCustomFields(): void
{
try {
$customFields = [
'your_plugin_product_field' => [
'type' => CustomFieldTypes::TEXT,
'name' => 'your_plugin_product_field',
'label' => [
'de-DE' => 'Your Plugin Product Field',
'en-GB' => 'Your Plugin Product Field'
],
'config' => [
'componentName' => 'sw-field',
'type' => 'text',
'placeholder' => 'Enter custom text here'
]
]
];
foreach ($customFields as $name => $field) {
$this->registerCustomField($name, $field);
}
} catch (\Exception $e) {
$this->logger->error('Failed to install custom fields', ['exception' => $e]);
throw $e;
}
}
private function registerCustomField(string $name, array $field): void
{
$existing = $this->customFieldRepository->search(
(new \Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria())
->addFilter(new \Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter('name', $name)),
\Shopware\Core\Framework\Context::createDefaultContext()
);
if ($existing->getTotal() > 0) {
return;
}
$this->customFieldRepository->create([
[
'name' => $name,
'type' => $field['type'],
'label' => $field['label'],
'config' => $field['config'],
'entity' => 'product'
]
], \Shopware\Core\Framework\Context::createDefaultContext());
}
}
Step 4: Update the Product Entity
To make your custom field available in the product entity, you need to extend the entity definition. Create a new file in your plugin's src/Core/Content/Product directory:
<?php declare(strict_types=1);
namespace YourPlugin\Core\Content\Product;
use Shopware\Core\Content\Product\ProductDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\Field\CustomField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
use Shopware\Core\Framework\DataAbstractionLayer\Field\TextField;
class ProductCustomFieldExtension extends ProductDefinition
{
public function extendFields(): void
{
$this->addCustomField('your_plugin_product_field', new TextField(
'your_plugin_product_field',
'yourPluginProductField'
));
}
}
Step 5: Configure the Extension
Register your extension in the plugin's services.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="YourPlugin\Service\ProductCustomFieldService">
<argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\CustomField\CustomFieldRepository"/>
<argument type="service" id="database_connection"/>
<argument type="service" id="logger"/>
</service>
<service id="YourPlugin\Core\Content\Product\ProductCustomFieldExtension">
<tag name="shopware.entity.extension"/>
</service>
</services>
</container>
Step 6: Handle Data Migration
If you're adding custom fields to an existing installation, you'll need to handle data migration. Create a migration file:
<?php declare(strict_types=1);
namespace YourPlugin\Migration;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;
class Migration1678901234AddCustomFieldToProduct extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1678901234;
}
public function update(Connection $connection): void
{
// Add custom field to product table
$sql = "ALTER TABLE `product`
ADD COLUMN IF NOT EXISTS `your_plugin_product_field` VARCHAR(255) DEFAULT NULL";
$connection->executeStatement($sql);
}
public function updateDestructive(Connection $connection): void
{
// No destructive changes needed for this example
}
}
Step 7: Update Administration Panel
To make your custom field visible in the administration panel, you need to extend the product detail view. Create a JavaScript file in your plugin's Resources/app/administration/src/module/sw-product/component directory:
// Resources/app/administration/src/module/sw-product/component/custom-field/sw-product-custom-field.vue
export default {
name: 'sw-product-custom-field',
props: {
product: {
type: Object,
required: true
}
},
data() {
return {
customField: this.product.yourPluginProductField || ''
};
},
methods: {
saveCustomField() {
this.$emit('custom-field-change', {
field: 'your_plugin_product_field',
value: this.customField
});
}
}
};
Step 8: Testing and Validation
After implementing your custom field, it's essential to test the functionality thoroughly. Create a simple test to verify that your custom field works correctly:
<?php declare(strict_types=1);
namespace YourPlugin\Tests;
use PHPUnit\Framework\TestCase;
use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour;
class ProductCustomFieldTest extends TestCase
{
use IntegrationTestBehaviour;
public function testCustomFieldIsAvailable(): void
{
$container = $this->getContainer();
// Test that custom field can be retrieved
$productRepository = $container->get('product.repository');
$customFieldRepository = $container->get('custom_field.repository');
$criteria = new \Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria();
$criteria->addFilter(new \Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter('name', 'your_plugin_product_field'));
$result = $customFieldRepository->search($criteria, \Shopware\Core\Framework\Context::createDefaultContext());
static::assertGreaterThan(0, $result->getTotal());
}
}
Performance Considerations
When working with custom fields in Shopware 6.7, consider these performance implications:
- Database Indexing: Ensure proper indexing on frequently queried custom field columns
- Caching Strategy: Implement appropriate caching for custom field data
- Query Optimization: Avoid unnecessary joins when retrieving product data with custom fields
Best Practices
- Naming Conventions: Use consistent naming conventions that clearly indicate the plugin context
- Documentation: Document your custom fields thoroughly for future maintenance
- Validation: Implement proper validation for custom field values
- Backward Compatibility: Ensure your implementation doesn't break existing functionality
Conclusion
Adding custom fields to the Product entity in Shopware 6.7 is a powerful way to extend product functionality without modifying core code. By following the steps outlined in this guide, you can create robust custom fields that integrate seamlessly with Shopware's architecture.
The key advantages of this approach include:
- Maintaining clean separation between core and custom functionality
- Enabling non-developers to manage custom field values through the administration panel
- Leveraging Shopware's built-in custom field management capabilities
- Ensuring future compatibility with platform updates
Remember to test your implementation thoroughly and consider performance implications when dealing with high-volume product data. With proper planning and execution, custom fields can significantly enhance your Shopware 6.7 store's functionality while maintaining system integrity.
This implementation provides a solid foundation for extending product entities in Shopware 6.7, and you can build upon it to create more complex custom field scenarios as needed for your specific requirements.