2

I'm new to Magento 2 and I'm trying to add a multiselect of all (for now) CMS pages to the CMS pages in Magento 2. What I mean by this is that when creating/editing a CMS page, there should be an extra field with all (other) CMS pages from which you can select multiple (or just one or none at all), just like you have a multiselect for the Store View field. I have got it to work to render the multiselect and you can select multiple, but upon saving it shows a message that "something" went wrong. Example of the fields: CMS page multiselect

I've also created an upgrade script which has successfully created an extra field in the cms_page table. I can still save pages, but only when I don't select any CMS pages.

Here's my //view/adminhtml/ui_component/cms_page_form.xml

<?xml version="1.0" encoding="UTF-8"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <fieldset name="search_engine_optimisation">
        <field name="redirect_to">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magento\Cms\Model\Config\Source\Page</item>
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">int</item>
                    <item name="label" xsi:type="string" translate="true">Redirect to pages</item>
                    <item name="formElement" xsi:type="string">multiselect</item>
                    <item name="source" xsi:type="string">page</item>
                    <item name="dataScope" xsi:type="string">redirect_to</item>
                    <item name="default" xsi:type="string">0</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                </item>
            </argument>
        </field>
    </fieldset>
</form>

What am I missing or doing wrong? For some reason, I've gotten it to work but it would only save one, never multiple, but I can't seem to reproduce that anymore.

What I'd like to achieve is for it to save all selected options as a JSON encoded string with the store id as key and the URL as value. How would I go about doing this?

Any help is much appreciated.

1

First of all you need to change option class for your requirement. So make following way.

Change <item name="options" xsi:type="object">Magento\Cms\Model\Config\Source\Page</item>

to

<item name="options" xsi:type="object">VendorName\ModuleName\Model\Config\Source\Page</item>



namespace VendorName\ModuleName\Model\Config\Source;

class Page extends \Magento\Cms\Model\Config\Source\Page
{
    /**
     * To option array
     *
     * @return array
     */
    public function toOptionArray()
    {
        if (!$this->options) {
            $emptyOption[] = [
                'value' => 'all',
                'label' => 'All'
            ];
            $this->options = $this->collectionFactory->create()->toOptionIdArray();

            $this->options = array_merge($emptyOption, $this->options);
        }

        return $this->options;
    }
}

Now you can able to see 'All' option inside multiselect element.

For save:

Create a plugin for this: VendorName/ModuleName/etc/adminhtml/di.xml


<?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\Cms\Controller\Adminhtml\Page\PostDataProcessor">
        <plugin name="PostDataProcessor_Save" type="VendorName\ModuleName\Plugin\Controller\Adminhtml\Page\PostDataProcessor" sortOrder="1"/>
    </type>
    <type name="Magento\Cms\Model\Page\DataProvider">
        <plugin name="DataProvider_Load" type="VendorName\ModuleName\Plugin\Model\Page\DataProvider" sortOrder="1"/>
    </type>
</config>

VendorName/ModuleName/Plugin/Controller/Adminhtml/Page/PostDataProcessor.php


namespace VendorName\ModuleName\Plugin\Controller\Adminhtml\Page;

class PostDataProcessor
{
    public function aroundFilter(
        \Magento\Cms\Controller\Adminhtml\Page\PostDataProcessor $subject,
        \Closure $proceed,
        $data
    ) {

        if(isset($data['redirect_to']) && ($data['redirect_to'])) {
            $data['redirect_to'] = json_encode($data['redirect_to']);
        }

        return $proceed($data);
    }
}

For edit:

VendorName/ModuleName/Plugin/Model/Page/DataProvider.php


namespace VendorName\ModuleName\Plugin\Model\Page;


class DataProvider
{
    /**
     * Get data
     *
     * @return array
     */
    public function afterGetData(
        \Magento\Cms\Model\Page\DataProvider $subject,
        $result
    ) {
        if (is_array($result)) {
            foreach ($result as &$item) {
                if(isset($item['redirect_to']) && ($item['redirect_to'])) {
                    $item['redirect_to'] = json_decode($item['redirect_to']);
                }
            }
        }

        return $result;
    }
}
  • this works great for editing already existing CMS pages, but when I want to create a new CMS page, it throws the following error: – Nico V Sep 15 '17 at 7:50
  • Check updated answer. – Sohel Rana Sep 15 '17 at 8:01
  • Sorry about the previous comment, it got cut of, here's the full comment: This works great for editing already existing CMS pages, but when I want to create a new CMS page, it throws the following error: Warning: Invalid argument supplied for foreach() in /var/www/app/code/Phpro/CmsRedirect/Plugin/Model/Page/DataProvider.php on line 25. It looks like somethiing was wrong wit the foreach ($result as &$item) {. To circumvent this I wrote a check around that to check if there is a $result and now it works great. Many, many thanks for the quick and elaborate response! – Nico V Sep 15 '17 at 8:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.