Custom fields in Shopware 6
What are custom fields in Shopware 6?
Custom fields are a Shopware 6 feature, that allows us to extend various entities like customers, products or categories, very easily. And by extending I mean storing and retrieving additional data.
Normally, if we need a new parameter for an entity like a product or a customer, we would probably add a new column to the corresponding table or a new table and a foreign key. And that is perfectly fine, but might get a bit complicated with more such items. This is where the custom fields come in handy. They allow us to store multiple variables of various types in an existing column. No additional tables or code needed, just work with existing structure and use standard Shopware 6 methods to set and get the data.
Sounds too good to be true? Well, the custom fields have their limits and disadvantages, for sure, but so far my experience with them was very positive. For a majority of tasks, concerning the storing and retrieving extra information to individual records, they are the best way to go. So let us take a closer look at them.
How are the custom fields stored and named?
Custom fields are stored in the database as JSON fields. JSON is a format, that Shopware 6 uses internally quite a lot and is able to store a relatively complex data, including nested arrays. Here is a small example of such data:
1 2 3 4 5 |
{ "someString": "90 mm", "someIds": ["276","277"], "someAssocArray": {"key_01": "3.3 g", "key_02": "APPROXIMATELY"} } |
The name of the column in any database table is always custom_fields. If you create your own table and want to have custom JSON data field in there, it is definitely a good idea to name the column custom_fields as well.
For which entities are custom fields available?
With some effort, you could actually create and use them on any table in Showare 6. However, most people will never need this, because for most needs, the custom fields are already in place in the most used tables.
Here is a list of tables from the Shopware 6 database, containing the custom_fields column:
- category_translation
- cms_block
- cms_page_translation
- cms_section
- cms_slot_translation
- country_state_translation
- country_translation
- currency_translation
- customer
- customer_address
- customer_group_translation
- delivery_time_translation
- document
- document_type_translation
- google_shopping_account
- google_shopping_merchant_account
- integration
- language
- locale_translation
- mail_template_translation
- mail_template_type_translation
- media_default_folder
- media_folder
- media_folder_configuration
- media_thumbnail
- media_thumbnail_size
- media_translation
- newsletter_recipient
- number_range_translation
- number_range_type_translation
- order
- order_address
- order_customer
- order_delivery
- order_delivery_position
- order_line_item
- order_transaction
- payment_method_translation
- plugin_translation
- product_configurator_setting
- product_manufacturer_translation
- product_media
- product_price
- product_stream_filter
- product_stream_translation
- product_translation
- property_group_option_translation
- property_group_translation
- rule
- rule_condition
- sales_channel_domain
- sales_channel_translation
- sales_channel_type_translation
- seo_url
- seo_url_template
- shipping_method_price
- shipping_method_translation
- snippet
- snippet_set
- state_machine_state_translation
- state_machine_transition
- state_machine_translation
- tax
- theme_translation
- unit_translation
- user
- user_access_key
If you do not believe me or you have a different version of Shopware, you can take a look yourself. Just run the following SQL command on the Shopware 6 database and you will get the list of tables, that contain the custom_fields column:
SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME IN ('custom_fields') AND TABLE_SCHEMA='your_shopware_database_name';
Note, that for some entities like product, the custom fields are actually present in the translation table, like for example product_translation. I suppose it works like this, so that you can have different custom data for different language versions of an item. That makes sense, but you also have to keep this in mind and make sure to save the data for all the language versions, even if they do not differ at all.
How to write to custom fields?
Writing to custom fields is super easy. Since they are just another column in the database, you only have to use the appropriate method (update, upsert, ..) on the repository and bring your custom data with it. As long as it is a string, number or an array, you do not have to do any conversions or preparations – just add them under the key customFields to the array, that you pass to the repository’s writing method and boom – your custom data are saved in the column custom_fields in the database table, corresponding to that repository.
Example:
1 2 3 4 5 6 |
$this->productRepository->update( [ [ 'id' => $productId, 'weight' => 777, 'customFields' => ['testNr' => rand(0,10), 'testBool' => true, 'testArray' => [ 'key_01' => 'value 01', 'key_02' => 'value 02' ] ] ] ] , $event->getContext() ); |
How to read from custom fields?
Of course we need to retrieve the values form the custom fields as well. This example will show you how:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//get some product data from the repository $products = $this->productRepository->search($criteria, $context)->getElements(); //go through each product foreach ($products as $product) { //get the custom fields value from the product $productCustomFields = $product->getCustomFields(); //get one particular value from the custom fields if (isset($productCustomFields['someValue'])) { //do something with someValue } //go through all the values from the custom fields if ($productCustomFields) { foreach ($productCustomFields as $key => $value) { //do something with this value } } } |
As you can see, you can easily get the custom fields contents, but you have to know your data structure, to be able to browse through them and retrieve precisely what you want. Keeping the custom fields structure as simple as possible is very advisable, but if you need a deeply nested array with various data types, you can do that – just keep in mind, that the logic in your code will have to be a bit more complicated as well.
How to filter and search by custom fields?
In some cases, you may want to use the custom fields for filtering certain items. That is not a problem as well, as the next example shows:
1 2 3 4 5 6 7 |
$data = $this->countryRepository->search( (new Criteria())->addFilter(new EqualsFilter('customFields.euMember', 1)), $event->getContext() )->getElements(); foreach ($data as $country) { //do something with the selected EU member country } |
In this case, we are looking for all countries in the country repository, that we have marked with a variable euMember like this:
1 2 3 4 5 6 7 8 9 10 11 |
//somewhere up here we have selected EU member countries //prepare the data array for writing to the database foreach ($countries as $id => $value) { $data[] = [ 'id' => $id, 'customFields' => [ 'euMember' => 1 ] ]; } //save the data to the database if ($data) { $this->countryRepository->update($data, $this->context); } |
The database table custom_field
In Shopware 6 database, there is a table called custom_field. As the name suggests, it contains the list of custom_field’s values, that the system knows about and stores their configuration information. You do not necessarilly need to put your values in here, if you just work with them in your plugin, but if you want to expose them to the user in a standard way (for example for user to be able to edit them in Administration), then it is necessary to have them here.
The speed of the custom fields
At first, I was afraid, that on larger data sets, especially those, containing longer texts, the custom fields would be slow. However, that is not the case. I have created an example script, where I filled the custom fields in the product table with loads of text in arrays and it took me really a big effort to actually be able to cause any troubles. Obviously the system can be overloaded by this, but those were insane amounts of data, that I can not imagine anyone would use in a real world use case. Under normal curcumstances, I can easily recommend using custom fields to store additional information to an entity.
Comparison with Magento
Just a sidenote here, regarding comparison with Magento, another well known e-commerce system. I like the way the custom fields are implemented in Shopware 6, because they are unified. They work exactly the same way, if you use them for products or categories or whatever. In Magento, you do have some options to store custom data and arguably some of them are more robust, mainly thanks to EAV model. However not all are available everywhere, they are named differently (like additional_data, additional_information etc.) and the way you work with them differs from case to case. This in turn results in longer development times, less sustainability and such stuff is more prone to errors as well. In Shopware 6, it is simply much easier and in the end, saves time to developers and money to shop owners. That is just my opinion anyway, based on my personal experience with both Magento 2 and Shopware 6. If you also worked with both systems, let me know your opinion in the comments below!