Quick Tip: How To Use Image, WYSWYG or any other non-standard field in Magento2 widget

Let’s keep this short and go straight to the task.

Imagine that you are working on a custom widget in Magento2 that should offer image or wysiwyg input fields. As you already know, Magento extensively uses configuration files, but what is new in Magento2 is that each config file is validated against a schema specific to its type.

Extensions define their widgets in widget.xml file, which is validated against Magento_Widget:etc/widget.xsd. By default, the following input types are allowed:

  • text
  • select
  • multiselect

So, we need a way to introduce new types.

Step 1: Register new SchemaLocator in di.xml

Create a di.xml file under: YOUR_VENDOR/YOUR_MODULE/etc/ with the following code:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Widget\Model\Config\Reader">
        <arguments>
            <argument name="schemaLocator" xsi:type="object">YOUR_VENDOR\YOUR_MODULE\Model\Config\SchemaLocator</argument>
        </arguments>
    </type>
</config>

This code will basically let Magento know to use our SchemaLocator, and not the default one.

Step 2: Introduce new validation schemas within SchemaLocator

Create a file: YOUR_VENDOR/YOUR_MODULE/Model/Config/SchemaLocator.php:

<?php

namespace YOUR_VENDOR\YOUR_MODULE\Model\Config;

use Magento\Framework\Module\Dir;

class SchemaLocator extends \Magento\Widget\Model\Config\SchemaLocator
{
    public function __construct(\Magento\Framework\Module\Dir\Reader $moduleReader)
    {
        parent::__construct($moduleReader);
        $etcDir = $moduleReader->getModuleDir(\Magento\Framework\Module\Dir::MODULE_ETC_DIR, 'YOURVENDOR_YOURMODULE');
        $this->_schema = $etcDir . '/widget.xsd';
        $this->_perFileSchema = $etcDir . '/widget_file.xsd';
    }
}

Instead of using Magento_Widget:widget.xsd and Magento_Widget:widget_file.xsd, we are saying: Use YOURVENDOR/YOURMODULE:widget.xsd and YOURVENDOR/YOURMODULE:widget_file.xsd files.

Step 3: Extend widget.xsd and widget_file.xsd

Create these two files in your extension’s etc directory:

widget.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:include schemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd"/>
    <xs:complexType name="editor">
        <xs:annotation>
            <xs:documentation>Parameter type which is WYSIWYG editor</xs:documentation>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="parameterType">
                <xs:sequence>
                    <xs:element name="value" type="translatableType" minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="image">
        <xs:annotation>
            <xs:documentation>Parameter type which is image</xs:documentation>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="parameterType">
                <xs:sequence>
                    <xs:element name="value" type="translatableType" minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
</xs:schema>

widget_file.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:include schemaLocation="urn:magento:module:Magento_Widget:etc/widget_file.xsd"/>
    <xs:complexType name="editor">
        <xs:annotation>
            <xs:documentation>Parameter type which is WYSIWYG editor</xs:documentation>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="parameterType">
                <xs:sequence>
                    <xs:element name="value" type="translatableType" minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="image">
        <xs:annotation>
            <xs:documentation>Parameter type which is image</xs:documentation>
        </xs:annotation>
        <xs:complexContent>
            <xs:extension base="parameterType">
                <xs:sequence>
                    <xs:element name="value" type="translatableType" minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
</xs:schema>

In both files, we are adding new types on top of the default ones.

Step 4: Make sure the extension is loaded AFTER Magento_Widget

In case this does not work, double check if Magento_Widget is loaded before your extension. As a quick check, you can scan magento2_root/etc/config.php and move your extension to the very bottom of the page. The clean solution would be to add a sequence in your extension’s module.xml.

That’s it! Please keep in mind that this is my own solution and I don’t have a clue if it’s in line with best practices. The only goal I had is not to touch the core. If you have a better idea for this, please advice either here in comments section, or in StackExchange thread.

 

Milan Stojanov is a certified Magento developer and Tuts+ author on Magento Fundamentals premium course. He likes to read and write about web development.

  • eduardo

    Hi Milan, had you manage to make it work with images? I am having problems to get the image parameter in the template whith: $image = $this->getData(‘image’); I only get “{{media url=”

    • Milan Stojanov

      Hi @disqus_V4rsvVuTXP:disqus! This tutorial explains how to make it possible for images fields to be added to widgets. However, as you probably know – you need a logic in your extension that will accept image from the widget form, upload it and save to your objects. There’s much more to be done here.

      • eduardo

        Hi @milan_stojanov:disqus thanks for answer, yes i am new with m2 but not that terrible 🙂 I made the widget pick the different fields and kind of work that part is not the problem. But i was curious if you tested with images because it is some kind of problem for magento to get the image parameter of the widget this is the result i get after saving the pages: {{widget type=”GPMDWidgetsBlockWidgetOneCard” content=”the content” image=”{{media url=”wysiwyg/24.png”}}”}} and i can get content fine but not image. Thanks anyway

  • Silvan

    Hi Milan, I followed your quick-tip and it works great. The problem is that I do not have a clue how to implement this (the logic itself). Could you please create a tutorial on how to implement the logic in an extension? Thanks!