Technical products usually have many attributes, such as dimensions, weight, operating conditions, and other special interfaces or software requirements. These are, however, not static: existing properties can be removed, or new properties can be added at any time – for example when new products or product groups are introduced.
PIM systems for product management
In case of very high numbers of products, so-called Product Information Management Systems (PIM systems) are often used to manage product attributes and assets. These systems usually offer an API to integrate products into websites using Content Management Systems like TYPO3.
Below a certain number of products, however, the introduction of a PIM system can be a big investment. Nonetheless, there is a desire to manage products in a structured manner and to display them on the website. How to achieve this using TYPO3 will be shown in this example.
Records with static attributes
Let’s take the simple case: Products should be saved in TYPO3 as individual records. To do this, you could create a table for product objects that contain the desired properties in individual database columns.
This is, of course, not a flexible solution: new properties require a migration of the table, so properties can not be edited by the editor. Dynamic storage of properties via FlexForm or JSON in a single column would be conceivable, but raises new questions: Where does the definition of these properties come from? How can be controlled which properties are valid for the current product group at all?
The TYPO3 core does not provide a viable solution.
The concept of property groups
Product attributes can usually be grouped into meaningful units, such as “dimensions”, “connections” or “downloads”, which are then valid for certain product groups. Some of these groups are applicable to all products, but some are only applicable to specific product groups.
In the following solution, central editing of attributes and property groups is possible. The property groups are then assigned to these product groups, so that only the valid attributes are available and editable on the products in the backend form.
Dynamic properties with “flux”
The extension “flux” offers the possibility to provide properties via Fluid templates, TypoScript or PHP and store these values as flexform data. Using the PHP API, we take advantage of these features to realize dynamic attributes and property groups.
The Domain Model in TYPO3
We need the following objects with corresponding database tables:
- Product (for products)
- ProductGroup (for product groups)
- Property (for attributes)
- PropertyGroup (for property groups)
For all these objects, corresponding Models (with getter and setter methods for the static properties), Repositories and TCA are available.
To store dynamic properties, the product table requires a column “pi_flexform” with a TCA configuration of type “flex”.
Then “flux” comes into play: In order for TYPO3 to display the appropriate backend form fields dynamically, we only need a custom Flux Provider:
Classes/Provider/ProductConfigurationProvider.php
<?php
namespace Medienreaktor\Products\Provider;
use FluidTYPO3\Flux\Provider\AbstractProvider;
use FluidTYPO3\Flux\Provider\ProviderInterface;
class ProductConfigurationProvider extends AbstractProvider implements ProviderInterface
{
/**
* @var string
*/
protected $tableName = 'tx_products_domain_model_product';
/**
* @var string
*/
protected $fieldName = 'pi_flexform';
/**
* productRepository
*
* @var \Medienreaktor\Products\Domain\Repository\ProductRepository
* @inject
*/
protected $productRepository = null;
/**
* @param array $row
* @return \FluidTYPO3\Flux\Form|NULL
*/
public function getForm(array $row)
{
$form = \FluidTYPO3\Flux\Form::create();
$form->setName('dynamicProperties');
$product = $this->productRepository->findByUid($row['uid']);
if ($product) {
$productGroup = $product->getProductGroup();
// Create a sheet for each property group
foreach ($productGroup->getPropertyGroups() as $group) {
$sheet = $form->createContainer(
'Sheet',
$group->getUid(),
$group->getName()
);
// Create a field for each property
foreach ($group->getProperties() as $property) {
// The property holds the field type, e.g. 'Input'
$sheet->createField(
$property->getType(),
$property->getUid(),
$property->getName()
);
}
}
}
return $form;
}
}
Registration of the provider in ext_localconf of the extension:
ext_localconf.php
<?php
defined('TYPO3_MODE') || die('Access denied.');
call_user_func(
function($extKey)
{
\FluidTYPO3\Flux\Core::registerConfigurationProvider(\Medienreaktor\Products\Provider\ProductConfigurationProvider::class);
...
},
$_EXTKEY
);
This Provider renders the backend form fields for the “pi_flexform” field by querying the property groups of the associated product group lines of the associated product group and creating the appropriate input fields for them.
In the Model of the product, we can retrieve these dynamic properties and provide them for template rendering:
Classes/Domain/Model/Product.php
<?php
namespace Medienreaktor\Products\Domain\Model;
class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
...
public function getDynamicProperties() {
$flexFormContent = $this->piFlexform;
$flexFormService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Service\\FlexFormService');
$fields = $flexFormService->convertFlexFormContentToArray($flexFormContent);
return $fields;
}
...
}
The corresponding input fields are displayed in the TYPO3 backend, depending on the assigned property groups.
For our customer using this solution, we have also implemented inheritance in a similar way (product properties can also be edited at the product group level and these are then automatically inherited to the products or can be overwritten there) – for the sake of simplicity, we have removed this code in the above examples.
Conclusion and download
The extension “flux” offers features that go far beyond the capabilities of the TYPO3 core and make countless other complex applications possible – for example a dynamic configuration of content elements by the editor (similar to DCE or GridElements).