Magento 2 – Impossible to Get Widget Configuration Fix

blocksmagento2moduleparameterwidget

I'm developing a module with a widget.
I make my own widget.xml following some differents guide in the net.

But, I've configured the widget from the back-office and I can't retrieve the widget data. It always returns NULL and all the data seem to not exists.
See the widget.xml:

<?xml version="1.0" encoding="UTF-8"?>

<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="tata_toto_widget" class="TATA\TOTO\Block\Widget\Reco" is_email_compatible="true">
        <label translate="true">Tata Toto</label>
        <description>Tata Toto widget.</description>
        <parameters>
            <parameter name="label" xsi:type="text" required="false" visible="true">
                <label translate="true">Label</label>
                <description translate="true">
                    Define the title appearing above recommendations in the template.
                </description>
            </parameter>
            <parameter name="zone_id" xsi:type="text" required="true" visible="true">
                <label translate="true">Zone Id</label>
            </parameter>
            <parameter name="number_of_recommendation" xsi:type="text" required="true" visible="true">
                <label translate="true">Number of recommendations</label>
                <description translate="true">Define how many items will be shown on the template.</description>
                <value>5</value>
            </parameter>
            <parameter name="page_type" xsi:type="select" required="true" visible="true"
                       source_model="TATA\TOTO\Model\Config\Source\PageType">
                <label translate="true">Page type</label>
            </parameter>
            <parameter name="force_product" xsi:type="select" required="true" visible="true"
                       source_model="Magento\Config\Model\Config\Source\Yesno">
                <label translate="true">Force product recommendation</label>
            </parameter>
            <parameter name="attribute_name_sort" xsi:type="text" required="false" visible="true">
                <label translate="true">Attribute name to sort</label>
                <depends>
                    <parameter name="page_type" value="Sort" />
                </depends>
            </parameter>
            <parameter name="order" xsi:type="select" required="false" visible="true"
                       source_model="TATA\TOTO\Model\Config\Source\SortOrder">
                <label translate="true">Order to sort</label>
                <depends>
                    <parameter name="page_type" value="Sort" />
                </depends>
            </parameter>
            <parameter name="group_by" xsi:type="text" required="false" visible="true">
                <label translate="true">Group by attribute</label>
                <depends>
                    <parameter name="page_type" value="Catalog" />
                </depends>
            </parameter>
            <parameter name="template" xsi:type="select" required="true" visible="true">
                <label translate="true">Template</label>
                <description translate="true">
                    The template on which the reco will be done.
                </description>
                <options>
                    <option name="horizontal" value="TOTO_TATA::widget/horizontal.phtml" selected="true">
                        <label translate="true">Horizontal Grid Template</label>
                    </option>
                    <option name="vertical" value="TOTO_TATA::widget/vertical.phtml">
                        <label translate="true">Vertical Grid Template</label>
                    </option>
                </options>
            </parameter>
        </parameters>
    </widget>
</widgets>

So I linked the widget to the Block TATA\TOTO\Block\Widget\Reco, here the block's code:

<?php

namespace TATA\TOTO\Block\Widget;

use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
use Magento\Framework\View\Element\Template\Context;
use TATA\TOTO\Model\Api\RecoApi;
use Magento\Framework\Registry;

/**
 * Class Reco
 *
 * @package TATA\TOTO
 * @author Tata Developper Team <tech@tata.com>
 */
class Reco extends AbstractRecoBlock
{
    /* @var ProductCollectionFactory */
    protected $_productCollectionFactory;

    /* @var ProductCollection $_productCollection */
    protected $_productCollection = null;

    /* @var array $options */
    public $options = array();

    /**
     * Reco constructor.
     *
     * @param Context $context
     * @param Registry $registry
     * @param ProductCollectionFactory $productCollectionFactory
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        ProductCollectionFactory $productCollectionFactory,
        array $data = [])
    {
        $this->_productCollectionFactory = $productCollectionFactory;

        parent::__construct($context, $registry, $data);

        var_dump($this->_data);

        // Initializing options array.
        $this->options['productBiz'] = $this->retrieveActualProducts();
        $this->options['serverUrl'] = $this->getTotoUrl();
        $this->options['tenantId'] = $this->getTenant();
        $this->options['zoneId'] = $this->getZoneId();
        $this->options['pageType'] = $this->getPageType();
        $this->options['forceProduct'] = $this->getForceProduct();
        $this->options['numberOfRecommendation'] = $this->getNumberOfRecommendation();
        $this->options['order'] = $this->getOrder();
        $this->options['groupBy'] = $this->getGroupBy();
        $this->options['attributeNameSort'] = $this->getAttributeNameSort();

        var_dump($this->options); // See the output below
    }
// ...
}

array(10) { ["productBiz"]=> array(1) { [0]=> string(4) "WT09" } ["serverUrl"]=> string(33) "someUrl" ["tenantId"]=> int(157) ["zoneId"]=> NULL ["pageType"]=> NULL ["forceProduct"]=> NULL ["numberOfRecommendation"]=> NULL ["order"]=> NULL ["groupBy"]=> NULL ["attributeNameSort"]=> NULL }

All the NULL values are sets in the widget back-office. And I remove all my getX() functions for the initial "magic" function.

When I var_dump($this->_data); which is the array which must contains the widget data I have nothing.

array(0) { }

AbstractRecoBlock (which is extended from the above given block) is now totaly empty and extends from Magento\Framework\View\Element\Template and implements Magento\Widget\Block\BlockInterface.

Here one of the guide I've following from A to Z: https://gielberkers.com/creating-widgets-in-magento-2/.

Do you have any leads?

Thanks!

Best Answer

Ok, I found the error.

It's not possible to reach _data from the constructor (even if you execute the parent before). So you have to create a function wich will initialize your widget options and call it in the template (or _toHtml() I think). So I have the following code in my block and it works:

<?php

namespace TOTO\TATA\Block\Widget;

use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use TOTO\TATA\Model\Api\RecoApi;
use Magento\Framework\Registry;
use Magento\Widget\Block\BlockInterface;


/**
 * Class Reco
 *
 * @package TOTO\TATA
 * @author TOTO Developper Team <tech@toto.com>
 */
class Reco extends Template implements BlockInterface
{
    /* @var ProductCollectionFactory $_productCollectionFactory */
    protected $_productCollectionFactory;

    /* @var ProductCollection $_productCollection */
    protected $_productCollection = null;

    /* @var Registry $registry */
    protected $_registry;

    /* @var array $options */
    public $options = array();

    /**
     * Reco constructor.
     *
     * @param Context $context
     * @param Registry $registry
     * @param ProductCollectionFactory $productCollectionFactory
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        ProductCollectionFactory $productCollectionFactory,
        array $data = [])
    {
        parent::__construct($context, $data);

        $this->_productCollectionFactory = $productCollectionFactory;
        $this->_registry = $registry;
    }

    public function initializeOptions()
    {
        $this->options['zoneId'] = $this->getZoneId();
        $this->options['pageType'] = $this->getPageType();
        $this->options['forceProduct'] = $this->getForceProduct();
        $this->options['numberOfRecommendation'] = $this->getNumberOfRecommendation();
        $this->options['order'] = $this->getSortOrder();
        $this->options['groupBy'] = $this->getGroupBy();
        $this->options['attributeNameSort'] = $this->getAttributeNameSort();
    }
    // Code...
}

And here my template:

<?php

$this->initializeOptions();
var_dump($this->options);

It works well. I extends again my block from an abstract one with all getX() functions like this:

/**
 * Gets the "Label" data safely.
 *
 * @return string
 */
protected function getLabel()
{
    if (!$this->hasData('label')) {
        $this->setData('label', self::DEFAULT_LABEL);
    }
    return (string)$this->getData('label');
}

Hope this will help some people :)