• Home
  • -
  • Using Magento2's JsonEncoded class

Using Magento2's JsonEncoded class

So recently i wanted to add a new EAV attribute for customers so i could save some data to the customer based on some order information. While i could create a new resource the simpliest approach for now was to add a JSON encoded object to the table to hold some basic order stats information.

I went searching to find the seralized class that im used to from magento 1 and came across the Serialized to JSON data upgrade migration to move from the old seralized type to the new JsonEncoded type. Below i'll show you how i used this backend model with a custom customer EAV attribute.

First off i created my upgrade script to create the new EAV attribute:

/**
 * {@inhertidoc}
 */
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
 {
        $setup->startSetup();
        $eavSetup = $this->_eavSetupFactory->create(['setup' => $setup]);

        $eavSetup->addAttribute(
            $this->_customerEntityName,
            'customer_sales_graph',
            [
                'type' => 'static',
                'label' => 'Customer Sales Graph',
                'backend' => \Rob3000\Config\Model\Entity\Attribute\Backend\JsonEncoded::class,
                'required' => false,
                'adminhtml_only' => 1,
                'visible' => true,
                'user_defined' => true,
                'system' => 0,
                'sort_order' => 160,
                'position' => 160,
            ]
        );

        $customerEntity = $this->_eavConfig->getEntityType($this->_customerEntityName);
        $attributeSetId = $customerEntity->getDefaultAttributeSetId();
        $attributeSet = $this->_attributeSetFactory->create();
        $attributeGroupId = $attributeSet->getDefaultGroupId($attributeSetId);

        $this->_eavConfig->getAttribute($this->_customerEntityName, 'customer_sales_graph')
            ->addData([
                'attribute_set_id' => $attributeSetId,
                'attribute_group_id' => $attributeGroupId,
            ])
            ->save();

        // Here we need to add the `customer_sales_graph` to the customer entity.
        $setup->getConnection()->addColumn(
            $setup->getTable('customer_entity'),
            'customer_sales_graph',
            [
                'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                'nullable' => true,
                'unsigned' => true,
                'comment' => 'Customer order data',
            ]
        );
    }

First let me explain whats going on with the above, First off we are creating the EAV attribute and saving that. We have set the attribute as static so the next step is to update the column of the customer_entity table to add out custom column.

As you'll notice above i have the following as the backend model for the EAV attribute \Rob3000\Config\Model\Entity\Attribute\Backend\JsonEncoded::class rather than Magento\Eav\Model\Entity\Attribute\Backend\JsonEncoded, why you ask, Well by default the TEXT, BLOB and JSON columns don't allow a default value, so when you try to load your EAV attribute it calls Magento\Eav\Model\Entity\Attribute\Backend::afterLoad:

public function afterLoad($object)
    {
        parent::afterLoad($object);
        $attrCode = $this->getAttribute()->getAttributeCode();
        $object->setData($attrCode, $this->jsonSerializer->unserialize($object->getData($attrCode)));
        return $this;
    }

In the above the $attrCode is null, so when its passed to $this->jsonSerializer->unserialize in Magento\Framework\Serialize\Serializer::unserialize this throws an exception as it cant call json_decode on NULL:

 public function unserialize($string)
    {
        $result = json_decode($string, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \InvalidArgumentException('Unable to unserialize value.');
        }
        return $result;
    }

So we need to adjust the afterLoad method to account for this. To do so i have created a new custom backend model that extends Magento\Eav\Model\Entity\Attribute\Backend which does the following:

<?php

namespace Rob3000\Config\Model\Entity\Attribute\Backend;

class JsonEncoded extends \Magento\Eav\Model\Entity\Attribute\Backend\JsonEncoded {

    /**
     * {@inheritdoc}
     */
    public function afterLoad($object)
    {

        $attrCode = $this->getAttribute()->getAttributeCode();
        $attr = $object->getData($attrCode);

        if (is_null($attr)) {
            $object->setData($attrCode, 0);
        }

        parent::afterLoad($object);

    }

}

What im doing in the afterLoad method is checking to see if the value is null and then setting the value as 0 so the parent afterLoad can successfully call unserialze.