So, solution is write a UpdateData
script in the own module which change the default serialized values to the JSON (like Magento does it in the CatalogRules
module):
app/code/MageWorx/ShippingRules/Setup/UpgradeData.php
class UpgradeData implements UpgradeDataInterface
{
/**
* @var \Magento\Framework\App\ProductMetadata
*/
protected $productMetadata;
/**
* @var MetadataPool
*/
private $metadataPool;
/**
* @var \Magento\Framework\DB\AggregatedFieldDataConverter
*/
private $aggregatedFieldConverter;
/**
* UpgradeData constructor.
*
* @param MetadataPool $metadataPool
* @param ObjectManagerInterface $objectManager
*/
public function __construct(
MetadataPool $metadataPool,
ObjectManagerInterface $objectManager,
\Magento\Framework\App\ProductMetadata $productMetadata
) {
$this->productMetadata = $productMetadata;
$this->metadataPool = $metadataPool;
if ($this->isUsedJsonSerializedValues()) {
$this->aggregatedFieldConverter = $objectManager->get('Magento\Framework\DB\AggregatedFieldDataConverter');
}
}
/**
* @return bool
*/
public function isUsedJsonSerializedValues()
{
$version = $this->productMetadata->getVersion();
if (version_compare($version, '2.2.0', '>=') &&
class_exists('\Magento\Framework\DB\AggregatedFieldDataConverter')
) {
return true;
}
return false;
}
Here we use the ObjectManager
class because there is no such class \Magento\Framework\DB\AggregatedFieldDataConverter
in the magento versions < 2.2.
Then initialize the update method:
/**
* {@inheritdoc}
*/
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
// 2.0.2 - module version compatible with magento 2.2
if (version_compare($context->getVersion(), '2.0.2', '<') && $this->aggregatedFieldConverter) {
// Convert each entity values
$this->convertRuleSerializedDataToJson($setup);
$this->convertZoneSerializedDataToJson($setup);
}
$setup->endSetup();
}
and method for conversion:
/**
* Convert Zone metadata from serialized to JSON format:
*
* @param ModuleDataSetupInterface $setup
*
* @return void
*/
protected function convertZoneSerializedDataToJson(ModuleDataSetupInterface $setup)
{
$metadata = $this->metadataPool->getMetadata(ZoneInterface::class);
$this->aggregatedFieldConverter->convert(
[
new FieldToConvert(
SerializedToJson::class,
$setup->getTable(Zone::ZONE_TABLE_NAME),
$metadata->getLinkField(),
'conditions_serialized'
),
],
$setup->getConnection()
);
}
where:
ZoneInterface
- entity interface, usually located at the Module/Vendor/Api/Data/
Zone::ZONE_TABLE_NAME
- corresponding table name, like 'mageworx_shippingrules_zone'
string
conditions_serialized
- name of the field having a serialized data, which should be converted to JSON
To make it work it is necessary to have few important things:
Your interface should have corresponding model in the etc/di.xml
:
<preference for="MageWorx\ShippingRules\Api\Data\RuleInterface" type="MageWorx\ShippingRules\Model\Rule" />
Corresponding entity repository should be defined in the RepositoryFactory
in the etc/di.xml
(and this repository should exist):
<type name="Magento\Framework\Model\Entity\RepositoryFactory">
<arguments>
<argument name="entities" xsi:type="array">
<item name="MageWorx\ShippingRules\Api\Data\RuleInterface" xsi:type="string">MageWorx\ShippingRules\Api\RuleRepositoryInterface</item>
<item name="MageWorx\ShippingRules\Api\Data\ZoneInterface" xsi:type="string">MageWorx\ShippingRules\Api\ZoneRepositoryInterface</item>
</argument>
</arguments>
</type>
Your entity shoul be defined in the MetadataPool
(in etc/di.xml
):
<type name="Magento\Framework\EntityManager\MetadataPool">
<arguments>
<argument name="metadata" xsi:type="array">
<item name="MageWorx\ShippingRules\Api\Data\RuleInterface" xsi:type="array">
<item name="entityTableName" xsi:type="string">mageworx_shippingrules</item>
<item name="identifierField" xsi:type="string">rule_id</item>
</item>
<item name="MageWorx\ShippingRules\Api\Data\ZoneInterface" xsi:type="array">
<item name="entityTableName" xsi:type="string">mageworx_shippingrules_zone</item>
<item name="identifierField" xsi:type="string">entity_id</item>
</item>
</argument>
</arguments>
</type>
In corresponding resource model default serializer (class) should not be defined or should be instance of Magento\Framework\Serialize\Serializer\Json
(as default). It's stored in the $this->serializer
attribute of the resource model.
If something goes wrong during the load process of your model I'll recomend you to start debugging from resource model, especially from the magento/framework/Model/ResourceModel/AbstractResource.php
class from method protected function _unserializeField(DataObject $object, $field, $defaultValue = null)
where:
$object
- should be instance of your model
$field
- serialized field, like conditions_serialized
The following example is taken from the MageWorx Shipping Suite extension.