Update the page URL type

What’s in this topic

This topic describes how to extend the existing UrlInput component to implement a new link type, or update an existing one, for your module.

Overview

To update a page URL type, you must:

  1. Create the link class.
  2. Add the link to the di.xml file.
  3. Create the component’s JavaScript implementation.
  4. Create a controller to search the page.
  5. Create a controller to return the page or array by cmsPageId.

Create a .php file implementing the new link class in the module directory containing the applicable UI component, such as <your-module>/Ui/Component/UrlInput/.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

declare(strict_types=1);

namespace %your module namespace%;

use Magento\Framework\UrlInterface;

/** Provides configuration for url input with type CMS page */
class Page implements \Magento\Ui\Model\UrlInput\ConfigInterface
{
    /**
     * @var \Magento\Framework\UrlInterface
     */
    private $urlBuilder;
    /**
     * @param UrlInterface $urlBuilder
     */
    public function __construct(UrlInterface $urlBuilder)
    {
        $this->urlBuilder = $urlBuilder;
    }
    /**
     * {@inheritdoc}
     */
    public function getConfig(): array
    {
        return [
            'label' => __('Page'),
            'component' => '%link to .js component you will complete in next step%',
            'disableLabel' => true,
            'filterOptions' => true,
            'searchOptions' => true,
            'chipsEnabled' => true,
            'levelsVisibility' => '1',
            'options' => [],
            'sortOrder' => 45,
            'multiple' => false,
            'closeBtn' => true,
            'template' => 'ui/grid/filters/elements/ui-select',
            'searchUrl' => $this->urlBuilder->getUrl('%path for search controller%'),
            'filterPlaceholder' => __('Page Name'),
            'isDisplayEmptyPlaceholder' => true,
            'emptyOptionsHtml' => __('Start typing to find cms page'),
            'missingValuePlaceholder' => __('Cms Page with ID: %s doesn\'t exist'),
            'isDisplayMissingValuePlaceholder' => true,
            'validationUrl' => $this->urlBuilder->getUrl('%path for validation controller%'),
        ];
    }
}

Add the link to the di.xml file

Add the link class you just created to the di.xml file.

1
2
3
4
5
6
7
<type name="Magento\Ui\Model\UrlInput\LinksConfigProvider">
    <arguments>
        <argument name="linksConfiguration" xsi:type="array">
            <item name="page" xsi:type="string">%link to your class%</item>
        </argument>
    </arguments>
</type>

Create the component’s JavaScript implementation

Create the JavaScript implementation for the applicable UI component in your module-specific directory, such as <your-module>/view/<area>/web/js/form/element/page-ui-select.js.

<area> would be adminhtml, frontend, or base depending on where you are applying the component implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

define([
    'Magento_Ui/js/form/element/ui-select',
    'jquery',
    'underscore'
], function (Select, $, _) {
    'use strict';

    return Select.extend({
        defaults: {
            validationUrl: false,
            loadedOption: {},
            validationLoading: true
        },

        /** @inheritdoc */
        initialize: function () {
            this._super();

            this.validateInitialValue();

            return this;
        },

        /**
         * Validate initial value actually exists
         */
        validateInitialValue: function () {
            if (!_.isEmpty(this.value())) {
                $.ajax({
                    url: this.validationUrl,
                    type: 'GET',
                    dataType: 'json',
                    context: this,
                    data: {
                        cmsPageId: this.value()
                    },

                    /** @param {Object} response */
                    success: function (response) {
                        if (!_.isEmpty(response)) {
                            this.options([response]);
                            this.loadedOption = response;
                        }
                    },

                    /** set empty array if error occurs */
                    error: function () {
                        this.options([]);
                    },

                    /** stop loader */
                    complete: function () {
                        this.validationLoading(false);
                        this.setCaption();
                    }
                });
            } else {
                this.validationLoading(false);
            }
        },

        /** @inheritdoc */
        getSelected: function () {
            var options = this._super();

            if (!_.isEmpty(this.loadedOption)) {
                return this.value() === this.loadedOption.value ? [this.loadedOption] : options;
            }

            return options;
        },

        /**
         * Get path to current option
         *
         * @param {Object} data - option data
         * @returns {String} path
         */
        getPath: function (data) {
            var path = '';

            if (this.renderPath) {
                path = data.identifier || path;
            }

            return path;
        }
    });
});

Create a controller to search the page

Create a controller to search the page using a search key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

declare(strict_types=1);

namespace %path to your module namespace%;

use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\UrlInterface;

class Search extends \Magento\Backend\App\Action implements HttpGetActionInterface, HttpPostActionInterface
{
    /**
     * Authorization level of a basic admin session
     *
     * @see _isAllowed()
     */
    const ADMIN_RESOURCE = 'Magento_Cms::save';
    /**
     * @var \Magento\Framework\Controller\Result\JsonFactory
     */
    private $resultJsonFactory;
    /**
     * @var \Magento\Cms\Model\ResourceModel\Page\CollectionFactory
     */
    private $cmsCollectionFactory;
    /**
     * @param \Magento\Framework\Controller\Result\JsonFactory $resultFactory
     * @param \Magento\Cms\Model\ResourceModel\Page\CollectionFactory $cmsCollectionFactory
     * @param \Magento\Backend\App\Action\Context $context
     */
    public function __construct(
        \Magento\Framework\Controller\Result\JsonFactory $resultFactory,
        \Magento\Cms\Model\ResourceModel\Page\CollectionFactory $cmsCollectionFactory,
        \Magento\Backend\App\Action\Context $context
    ) {
        $this->resultJsonFactory = $resultFactory;
        $this->cmsCollectionFactory = $cmsCollectionFactory;
        parent::__construct($context);
    }
    /**
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute(): \Magento\Framework\Controller\ResultInterface
    {
        $searchKey = $this->getRequest()->getParam('searchKey');
        $pageNum = (int)$this->getRequest()->getParam('page');
        $limit = (int)$this->getRequest()->getParam('limit');
        /** @var \Magento\Cms\Model\ResourceModel\Page\Collection $cmsCollection */
        $cmsCollection = $this->cmsCollectionFactory->create();
        $cmsCollection->setCurPage($pageNum)->setPageSize($limit);
        $totalValues = $cmsCollection->getSize();
        $cmsCollection->addFilter('title', ['in'=> $searchKey]);
        $pagesById = [];
       /** @var \Magento\Cms\Model\Page $cmsPage */
        foreach ($cmsCollection as $cmsPage) {
            $cmsId = $cmsPage->getId();
            $pagesById[$cmsId] = [
                'value' => $cmsId,
                'label' => $cmsPage->getTitle(),
                'identifier' => sprintf(__('ID: %s'), $cmsId)
            ];
        }
        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
        $resultJson = $this->resultJsonFactory->create();
        return $resultJson->setData([
            'options' => $pagesById,
            'total' => empty($pagesById) ? 0 : $totalValues
        ]);
    }
}

Create a controller to return the page or array

Create a controller to return the page, or empty array if the option doesn’t exist, by the cmsPageId.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

declare(strict_types=1);

namespace %path to your module%;

use Magento\Framework\App\Action\HttpGetActionInterface;

class GetSelected extends \Magento\Backend\App\Action implements HttpGetActionInterface
{
    /**
     * Authorization level of a basic admin session
     *
     * @see _isAllowed()
     */
    const ADMIN_RESOURCE = 'Magento_Cms::save';
    /**
     * @var \Magento\Framework\Controller\Result\JsonFactory
     */
    private $resultJsonFactory;
    /**
     * @var \Magento\Cms\Model\ResourceModel\Page\CollectionFactory
     */
    private $cmsCollectionFactory;
    /**
     * GetSelected constructor.
     * @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory
     * @param \Magento\Cms\Model\ResourceModel\Page\CollectionFactory $cmsCollectionFactory
     * @param \Magento\Backend\App\Action\Context $context
     */
    public function __construct(
        \Magento\Framework\Controller\Result\JsonFactory $jsonFactory,
        \Magento\Cms\Model\ResourceModel\Page\CollectionFactory $cmsCollectionFactory,
        \Magento\Backend\App\Action\Context $context
    ) {
        $this->resultJsonFactory = $jsonFactory;
        $this->cmsCollectionFactory = $cmsCollectionFactory;
        parent::__construct($context);
    }
    /**
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute() : \Magento\Framework\Controller\ResultInterface
    {
        $cmsPageId = $this->getRequest()->getParam('cmsPageId');
        /** @var \Magento\Cms\Model\ResourceModel\Page\Collection $cmsPageCollection */
        $cmsPageCollection = $this->cmsCollectionFactory->create();
        $cmsPageCollection->addFilter('page_id', $cmsPageId);
        $option = [];
        if (!empty($cmsPageCollection->getFirstItem()->getData())) {
            $cmsPage = $cmsPageCollection->getFirstItem();
            $option = [
                'value' => $cmsPageId,
                'label' => $cmsPage->getTitle(),
                'identifier' => sprintf(__('ID: %s'), $cmsPageId)
            ];
        }
        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
        $resultJson = $this->resultJsonFactory->create();
        return $resultJson->setData($option);
    }
}