Fix Custom Module Data Save Array to String Exception in Magento 2

controllersdatabasemagento2model

I've made a custom module with a ui component in the admin. It's a grid that is filled with block data. 

When adding a block, and saving it in Controller/Adminhtml/Content/Save.php, exception
"Array to string conversion in /data/web/magento2/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php on line 3028" is given.

> Controller/Adminhtml/Content/Save.php

<?php 

    use Magento\Framework\App\Filesystem\DirectoryList;
    use Digitaq\HomepageContent\Model\BlockContent;

    class Save extends \Magento\Backend\App\Action
    {  
        public function execute()
        {    
            $data = $this->getRequest()->getPostValue();
            if ($data) {    
                $id = $this->getRequest()->getParam('id');
                $model = $this->_objectManager->create('Digitaq\HomepageContent\Model\BlockContent')->load($id);

                if (!$model->getId() && $id) {
                    $this->messageManager->addError(__('This item no longer exists.'));
                    return $resultRedirect->setPath('*/*/');
                }    
                $model->setData($data);    
                try {

                    $model->save();
                    //Display success message
                    $this->messageManager->addSuccess(__('The Frist Grid Has been Saved.'));
                    //clear previous data from session
    //                $this->_objectManager->get('Magento\Backend\Model\Session')->setFormData(false);

                    //If save & continue, head back
                    if ($this->getRequest()->getParam('back')) {
                        $this->_redirect('*/*/edit', array('id' => $model->getId(), '_current' => true));
                        return;
                    }
                    $this->_redirect('*/*/');
                    return;
                } catch (\Magento\Framework\Model\Exception $e) {
                    $this->messageManager->addError($e->getMessage());
                } catch (\RuntimeException $e) {
                    $this->messageManager->addError($e->getMessage());
                } catch (\Exception $e) {
                    $this->messageManager->addException($e, __('Something went wrong while saving the block.'));
                }

                $this->_getSession()->setFormData($data);
                $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('id')));
                return;
            }
            $this->_redirect('*/*/');
        }
    }

> Model/BlockContent.php

<?php

    class BlockContent extends \Magento\Framework\Model\AbstractModel
    {
        protected function _construct()
        {
            $this->_init('\Vendor\Module\Model\ResourceModel\BlockContent');
        }
    }

> Model/ResourceModel/BlockContent.php
<?php

    use Magento\Framework\DB\Select;
    use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

    class BlockContent extends AbstractDb
    {
        protected function _construct()
        {
            $this->_init('homepagecontent_blocks', 'id');
        }
    }

> Model/ResourceModel/BlockContent/Collection.php

<?php

    class Collection extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
    {
        protected function _construct()
        {
            $this->_init(
                'Vendor\Module\Model\BlockContent',
                'Vendor\Module\Model\ResourceModel\BlockContent'
            );
        }
    }

> Model/ResourceModel/BlockContent/Grid/Collection.php

<?php


    use Magento\Framework\View\Element\UiComponent\DataProvider\Document;
    use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as FetchStrategy;
    use Magento\Framework\Data\Collection\EntityFactoryInterface as EntityFactory;
    use Magento\Framework\Event\ManagerInterface as EventManager;
    use Psr\Log\LoggerInterface as Logger;

    class Collection extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
    {
        /**
         * @inheritdoc
         */
        protected $document = Document::class;

        /**
         * Initialize dependencies.
         *
         * @param EntityFactory $entityFactory
         * @param Logger $logger
         * @param FetchStrategy $fetchStrategy
         * @param EventManager $eventManager
         * @param string $mainTable
         * @param string $resourceModel
         */
        public function __construct(
            EntityFactory $entityFactory,
            Logger $logger,
            FetchStrategy $fetchStrategy,
            EventManager $eventManager,
            $mainTable = 'homepagecontent_blocks',
            $resourceModel = '\Vendor\Module\Model\ResourceModel\BlockContent'
        ) {
            parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel);
        }
    }

A var_dump of $data displays: 

    array (size=2)
      'form_key' => string 'KGogknDar22zgria' (length=16)
      'content' => 
        array (size=8)
      'id' => string '1241' (length=4)
      'css_grid_column_from' => string '2' (length=1)
      'css_grid_column_to' => string '2' (length=1)
      'css_grid_row_from' => string '1' (length=1)
      'css_grid_row_to' => string '3' (length=1)
      'content_type' => string 'slider' (length=6)
      'status' => string '1' (length=1)
      'content' => string '<p>asdgsad</p>' (length=14)

Could anyone help me trying to debug this?

Best Answer

Some kind of data which should have string-type was not converted by your model before saving.

I can give you a way how to debug it:

Open a core file /data/web/magento2/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php, find line 3028, and change line

$value = (string)$value;

to this one:

if (is_array($value)) {
    var_dump($value);
    die();
}
$value = (string)$value;

Note: preferred way is to use the xDebug for that purposes.

Reload a page. Now you can see a data which should be string but not an array. Find the corresponding column and modify your code: add data-convertor which will change data type to a desired one (string). Usually it is implode($value) or json_serrialize($value).

You can mark the content field as a serializable field in the corresponding resource model:

protected $_serializableFields = [ 'content' => [null, []] ]; 

This way magento resource model each time will convert this data during save\load process

PS: using xDebug you can add a conditional breakpoint:

conditional breakpoint