Magento2 | PWA | GraphQL

Magento2 homepage banner and product slider extension with countdown timer


This Homepage banner with product slider extension provides the below options along with your homepage slider.

1. Add Slick carousel on homepage by managing all the option and controls from admin.

2. Add Slider with slides.

3. Add title, content, URL, Position, Start date, end date and counter for slide.

4. Add countdown date from, date to, color and background color option.

5. Display product daily deal information on slider slide.

6. Add video for slide.

3. Add different images for slide. (Thumbnail Image, Image for desktop, Image for Mobile, Image for tablet)

You can find the complete module on GitHub at Magelearn_Slider

Or Check the below images for a better understanding of the functionality of this module.










Let's start it by creating a custom extension. 

Create a folder inside app/code/Magelearn/Slider

Add registration.php file in it:

<?php

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Magelearn_Slider',
    __DIR__
);
Add composer.json file in it:
{
    "name": "magelearn/magento2-module-slider",
    "description": "Magento2 homepage banner and product slider extension with countdown timer. Add YouTube, Vimeo and HTML Video on Homepage as slider. Also Display Products on slider with Countdown timer to display like a deals. Auto Enable/Disable slider with Cron job.",
    "require": {
        "magento/magento-composer-installer": "*",
        "php": "^7.2"
    },
    "type": "magento2-module",
    "version": "1.0.0",
    "license": [
        "proprietary"
    ],
    "authors": [
        {
            "name": "Vijay Rami",
            "email": "vijaymrami@gmail.com"
        }
    ],
    "autoload": {
        "files": [
            "src/registration.php"
        ],
        "psr-4": {
            "Magelearn\\Slider\\": "src"
        }
    },
    "keywords": [
        "magento2",
        "slider",
        "homepage"
    ],
    "minimum-stability": "dev",
    "prefer-stable": true
}
Add etc/module.xml file in it:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Magelearn_Slider" setup_version="1.0.0"/>
</config>
Now to manage the Slider and its slide data, we will create two different tables.

Add etc/db_schema.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="magelearn_slider_slider" resource="default" engine="innodb" comment="Slider entities table">
        <column xsi:type="int" name="slider_id" padding="10" unsigned="true" nullable="false" identity="true" comment="Entity Id" />
        <column xsi:type="text" name="title" nullable="true" comment="Slider title" />
        <column xsi:type="smallint" name="status" default="1" nullable="false" unsigned="true" comment="Slider status" />
        <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Created At" />
        <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" comment="Updated At" />
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="slider_id"/>
        </constraint>
        <index referenceId="MAGELEARN_SLIDER_SLIDER_SLIDER_ID" indexType="btree">
            <column name="slider_id"/>
        </index>
    </table>
    <table name="magelearn_slider_slider_item" resource="default" engine="innodb" comment="Slider items table">
        <column xsi:type="int" name="slide_id" padding="10" unsigned="true" nullable="false" identity="true" comment="Entity Id" />
        <column xsi:type="int" name="slider_id" padding="10" unsigned="true" nullable="false" comment="Parent slider Id" />
        <column xsi:type="text" name="title" nullable="true" comment="Slide title" />
        <column xsi:type="smallint" name="status" default="1" nullable="false" unsigned="true" comment="Slide status" />
        <column xsi:type="smallint" name="position" default="1" nullable="false" comment="Slide position" />
        <column xsi:type="text" name="url" nullable="true" comment="Slide URL" />
        
        <column xsi:type="text" name="content" nullable="true" comment="Slide content"/>
        <column xsi:type="smallint" name="video" default="0" nullable="false" unsigned="true" comment="Slide video type"/>
        <column xsi:type="text" name="video_content" nullable="true" comment="Slide video content"/>

        <column xsi:type="smallint" name="is_active_countdown" default="0" nullable="false" unsigned="true" comment="Countdown status"/>
        <column xsi:type="timestamp" name="countdown_date_from" nullable="true" default="NULL" comment="Countdown date from"/>
        <column xsi:type="timestamp" name="countdown_date_to" nullable="true" default="NULL" comment="Countdown date to"/>
        <column xsi:type="text" name="countdown_color" nullable="true" comment="Countdown color"/>
        <column xsi:type="text" name="countdown_background_color" nullable="true" comment="Countdown background color"/>

        <column xsi:type="smallint" name="show_daily_deal" default="0" nullable="false" unsigned="true" comment="Show daily deal"/>
        <column xsi:type="text" name="daily_deal_product_id" nullable="true" comment="Daily deal product ID"/>
        <column xsi:type="text" name="daily_deal_color" nullable="true" comment="Daily deal color"/>
        <column xsi:type="text" name="daily_deal_background_color" nullable="true" comment="Daily deal background color"/>
        <column xsi:type="text" name="daily_deal_top" nullable="true" comment="Daily deal top position"/>
        <column xsi:type="text" name="daily_deal_left" nullable="true" comment="Daily deal left position"/>

        <column xsi:type="text" name="image_mobile" nullable="true" comment="Slide mobile image" />
        <column xsi:type="text" name="image_thumbnail" nullable="true" comment="Slide thumbnail image" />
        <column xsi:type="text" name="image_small" nullable="true" comment="Slide small size image" />
        <column xsi:type="text" name="image_medium" nullable="true" comment="Slide medium size image" />

        <column xsi:type="timestamp" name="date_from" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Slide start date" />
        <column xsi:type="timestamp" name="date_to" on_update="false" nullable="true" default="CURRENT_TIMESTAMP" comment="Slide end date" />
        <column xsi:type="boolean" name="is_active" default="0" nullable="false" comment="Flag that is set to true, if slide should be visible based on dates range"/>

        <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Created At" />
        <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" comment="Updated At" />
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="slide_id"/>
        </constraint>
        <constraint xsi:type="foreign"
                    referenceId="MAGELEARN_SLIDER_SLIDER_ITEM_SLIDER_ID_MAGELEARN_SLIDER_SLIDER_SLIDER_ID"
                    table="magelearn_slider_slider_item"
                    column="slider_id"
                    referenceTable="magelearn_slider_slider"
                    referenceColumn="slider_id"
                    onDelete="CASCADE"/>
        <index referenceId="MAGELEARN_SLIDER_SLIDER_ITEM_SLIDE_ID" indexType="btree">
            <column name="slide_id"/>
        </index>
        <index referenceId="MAGELEARN_SLIDER_SLIDER_SLIDER_ID" indexType="btree">
            <column name="slider_id"/>
        </index>
        <index referenceId="MAGELEARN_SLIDER_SLIDER_SLIDER_ID_STATUS_IS_ACTIVE" indexType="btree">
            <column name="slider_id"/>
            <column name="status"/>
            <column name="is_active"/>
        </index>
    </table>
</schema>
Now to give some admin configuration options add file at etc/adminhtml/system.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="magelearn_modules" translate="label" sortOrder="10">
            <label>Magelearn Modules</label>
        </tab>
        <section id="magelearn_slider" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Slider</label>
            <tab>magelearn_modules</tab>
            <resource>Magelearn_Slider::config</resource>
            <group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General</label>
                <field id="is_enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Enable Module</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
            </group>
            <group id="display_settings" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Display settings</label>
                <field id="autoplay" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Enable autoplay</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="accessibility" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Accessibility</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <comment>It Enables tabbing and arrow key navigation</comment>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="autoplay_speed" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Slider autoplay speed</label>
                    <depends>
                        <field id="autoplay">1</field>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                    <comment>Time in milliseconds</comment>
                    <validate>validate-number validate-zero-or-greater</validate>
                </field>
                <field id="speed" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Slide/Fade animation speed</label>
                    <depends>
                        <field id="autoplay">1</field>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                    <comment>Time in milliseconds</comment>
                    <validate>validate-number validate-zero-or-greater</validate>
                </field>
                <field id="pause_on_focus" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Pause on focus</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="pause_on_hover" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Pause on hover</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="pause_on_dots_hover" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Pause on Dots hover</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <comment>Pause Autoplay when a dot is hovered</comment>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="slides_to_show" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Slides to show</label>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                    <validate>validate-number validate-zero-or-greater</validate>
                </field>
                <field id="slides_to_scroll" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Slides to scroll</label>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                    <validate>validate-number validate-zero-or-greater</validate>
                </field>
                <field id="show_arrows" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Show arrows</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="show_dots" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Show Dots</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="infinite_loop" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Infinite loop</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="lazy_load" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Lazy Load</label>
                    <source_model>Magelearn\Slider\Model\Config\Source\Lazyload</source_model>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
                <field id="css_ease" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>cssEase</label>
                    <comment>CSS3 Animation Easing. You can add values like (ease, linear or custom css animation)</comment>
                    <depends>
                        <field id="magelearn_slider/general/is_enabled">1</field>
                    </depends>
                </field>
            </group>
        </section>
    </system>
</config>

As per the highlighted code above add a file at app/code/Magelearn/Slider/Model/Config/Source/Lazyload.php

<?php
namespace Magelearn\Slider\Model\Config\Source;

use Magento\Framework\Data\OptionSourceInterface;
 
class Lazyload implements OptionSourceInterface
{
    /**
     * Return array of options as value-label pairs
     *
     * @return array Format: array(array('value' => '<value>', 'label' => '<label>'), ...)
     */
    public function toOptionArray()
    {
        return [
            ['value' => 'ondemand', 'label' => 'On-Demand'],
            ['value' => 'progressive', 'label' => 'Progressive']
        ];
    }
}

Also provide default configuration at etc/config.xml file

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <magelearn_slider>
            <general>
                <is_enabled>0</is_enabled>
            </general>
            <display_settings>
                <accessibility>1</accessibility>
                <autoplay>1</autoplay>
                <autoplay_speed>5000</autoplay_speed>
                <speed>300</speed>
                <css_ease>ease</css_ease>
                <pause_on_focus>0</pause_on_focus>
                <pause_on_hover>1</pause_on_hover>
                <pause_on_dots_hover>0</pause_on_dots_hover>
                <slides_to_show>1</slides_to_show>
                <slides_to_scroll>1</slides_to_scroll>
                <show_arrows></show_arrows>
                <show_dots>1</show_dots>
                <infinite_loop>1</infinite_loop>
                <lazy_load>ondemand</lazy_load>
            </display_settings>
        </magelearn_slider>
    </default>
</config>

Now, we will create di.xml file inside etc folder. 

This file will define the necessary nodes to display data in the admin grid.

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <virtualType name="MagelearnSliderSliderItemGrid" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
        <arguments>
            <argument name="mainTable" xsi:type="string">magelearn_slider_slider_item</argument>
            <argument name="resourceModel" xsi:type="string">Magelearn\Slider\Model\ResourceModel\Slide</argument>
        </arguments>
    </virtualType>
    <virtualType name="MagelearnSliderSliderEntityGrid" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
        <arguments>
            <argument name="mainTable" xsi:type="string">magelearn_slider_slider</argument>
            <argument name="resourceModel" xsi:type="string">Magelearn\Slider\Model\ResourceModel\Slider</argument>
        </arguments>
    </virtualType>
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="slide_grid_data_source" xsi:type="string">MagelearnSliderSliderItemGrid</item>
                <item name="slider_grid_data_source" xsi:type="string">MagelearnSliderSliderEntityGrid</item>
            </argument>
        </arguments>
    </type>
    <type name="Magelearn\Slider\Controller\Adminhtml\Slide\Image\Upload">
        <arguments>
            <argument name="imageUploader" xsi:type="object">Magelearn\Slider\SlideImageUpload</argument>
        </arguments>
    </type>
    <virtualType name="Magelearn\Slider\SlideImageUpload" type="Magelearn\Slider\Model\ImageUploader">
        <arguments>
            <argument name="baseTmpPath" xsi:type="string">slider/tmp/images</argument>
            <argument name="basePath" xsi:type="string">slider/images/upload</argument>
            <argument name="allowedExtensions" xsi:type="array">
                <item name="jpg" xsi:type="string">jpg</item>
                <item name="jpeg" xsi:type="string">jpeg</item>
                <item name="png" xsi:type="string">png</item>
                <item name="svg" xsi:type="string">svg</item>
                <item name="gif" xsi:type="string">gif</item>
            </argument>
        </arguments>
    </virtualType>
    <!-- preferences for Interfaces -->
    <preference for="Magelearn\Slider\Api\SlideRepositoryInterface" type="Magelearn\Slider\Model\SlideRepository" />
    <preference for="Magelearn\Slider\Api\SliderRepositoryInterface" type="Magelearn\Slider\Model\SliderRepository" />
    <preference for="Magelearn\Slider\Api\SliderManagementInterface" type="Magelearn\Slider\Model\SliderManagement" />
    <preference for="Magelearn\Slider\Api\Data\SlideInterface" type="Magelearn\Slider\Model\Slide"/>
    <preference for="Magelearn\Slider\Api\Data\SliderInterface" type="Magelearn\Slider\Model\Data\Slider" />
    <preference for="Magelearn\Slider\Api\Data\SlideSearchResultsInterface" type="Magento\Framework\Api\SearchResults" />
    <preference for="Magelearn\Slider\Api\Data\SliderSearchResultsInterface" type="Magento\Framework\Api\SearchResults" />
</config>

Before Proceed further, we will add etc/adminhtml/menu.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Magelearn_Slider::menu"
             title="Banner Slider"
             module="Magelearn_Slider"
             sortOrder="99"
             resource="Magelearn_Slider::menu"
             parent="Magento_Backend::content"
        />
        <add id="Magelearn_Slider::slider"
             title="Slider items"
             module="Magelearn_Slider"
             sortOrder="0"
             action="slider/slider/"
             resource="Magelearn_Slider::slider"
             parent="Magelearn_Slider::menu"
        />
    </menu>
</config>

Add etc/adminhtml/routes.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="slider" frontName="slider">
            <module name="Magelearn_Slider" />
        </route>
    </router>
</config>

Add etc/acl.xml file.

<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::content">
                    <resource id="Magelearn_Slider::menu" title="Banner Slider" sortOrder="99">
                        <resource id="Magelearn_Slider::slider" title="Manage sliders" sortOrder="0">
                            <resource id="Magelearn_Slider::slide" title="Manage slides" sortOrder="0" />
                        </resource>
                    </resource>
                </resource>
                <resource id="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="Magelearn_Slider::config" title="Slider" sortOrder="99" />
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

Now for backend management, We will start to create files as defined in etc/di.xml nodes.

Create Api/SliderRepositoryInterface.php file.

<?php

namespace Magelearn\Slider\Api;

use Magelearn\Slider\Api\Data\SliderInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;

interface SliderRepositoryInterface
{
    public function save(SliderInterface $slider): SliderInterface;

    public function getById($sliderId): SliderInterface;

    public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface;

    public function delete(SliderInterface $slider): void;

    public function deleteById($sliderId): void;
}

Create Model/SliderRepository.php file.

<?php

namespace Magelearn\Slider\Model;

use Magelearn\Slider\Api\Data\SliderInterface;
use Magelearn\Slider\Api\Data\SliderInterfaceFactory;
use Magelearn\Slider\Api\Data\SliderSearchResultsInterfaceFactory;
use Magelearn\Slider\Api\SliderRepositoryInterface;
use Magelearn\Slider\Model\ResourceModel\Slider\CollectionFactory as SliderCollectionFactory;
use Magelearn\Slider\Model\SliderFactory;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;

class SliderRepository implements SliderRepositoryInterface
{
    /** @var \Magelearn\Slider\Model\Slider */
    private $sliderServiceModel;

    /** @var SliderCollectionFactory */
    private $sliderCollectionFactory;

    /** @var CollectionProcessorInterface */
    private $collectionProcessor;

    /** @var SliderSearchResultsInterfaceFactory */
    private $searchResultsFactory;

    /** @var SliderInterfaceFactory */
    private $sliderAnemicModelFactory;

    /**
     * SliderRepository constructor.
     * @param SliderFactory $sliderFactory
     * @param SliderInterfaceFactory $sliderAnemicModelFactory
     * @param SliderCollectionFactory $sliderCollectionFactory
     * @param SliderSearchResultsInterfaceFactory $searchResultsFactory
     * @param CollectionProcessorInterface $collectionProcessor
     */
    public function __construct(
        SliderFactory $sliderFactory,
        SliderInterfaceFactory $sliderAnemicModelFactory,
        SliderCollectionFactory $sliderCollectionFactory,
        SliderSearchResultsInterfaceFactory $searchResultsFactory,
        CollectionProcessorInterface $collectionProcessor
    ) {
        $this->sliderServiceModel       = $sliderFactory->create();
        $this->sliderAnemicModelFactory = $sliderAnemicModelFactory;
        $this->sliderCollectionFactory  = $sliderCollectionFactory;
        $this->collectionProcessor      = $collectionProcessor;
        $this->searchResultsFactory     = $searchResultsFactory;
    }

    /**
     * @param SliderInterface $slider
     * @return SliderInterface
     * @throws \Exception
     */
    public function save(SliderInterface $slider): SliderInterface
    {
        return $this->sliderServiceModel->save($slider);
    }

    /**
     * @param $sliderId
     * @return SliderInterface
     */
    public function getById($sliderId): SliderInterface
    {
        return $this->sliderServiceModel->load($sliderId);
    }

    /**
     * @param SearchCriteriaInterface $searchCriteria
     * @return SearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface
    {
        $searchResults = $this->searchResultsFactory->create();
        $searchResults->setSearchCriteria($searchCriteria);
        /** @var \Magelearn\Slider\Model\ResourceModel\Slider\Collection $collection */
        $collection = $this->sliderCollectionFactory->create();

        $this->collectionProcessor->process($searchCriteria, $collection);

        $searchResults->setTotalCount($collection->getSize());
        $searchResults->setItems($collection->getItems());
        return $searchResults;
    }

    /**
     * @param SliderInterface $slider
     * @throws \Exception
     */
    public function delete(SliderInterface $slider): void
    {
        $this->sliderServiceModel->delete($slider);
    }

    /**
     * @param $sliderId
     * @throws \Exception
     */
    public function deleteById($sliderId): void
    {
        /** @var SliderInterface $anemicModel */
        $anemicModel = $this->sliderAnemicModelFactory->create();
        $anemicModel->setSliderId($sliderId);

        $this->sliderServiceModel->delete($anemicModel);
    }
}
Create Api/SlideRepositoryInterface.php file.
<?php

namespace Magelearn\Slider\Api;

use Magelearn\Slider\Api\Data\SlideInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;

interface SlideRepositoryInterface
{
    /**
     * Saves Slide
     *
     * @param SlideInterface $entity
     * @return SlideInterface
     * @throws CouldNotSaveException
     */
    public function save(SlideInterface $entity): SlideInterface;

    /**
     * Retrieves Slide by slide_id
     *
     * @param int $id
     * @return SlideInterface
     * @throws NoSuchEntityException
     */
    public function getById($id): SlideInterface;

    /**
     * Retrieves slide matching the specified criteria
     *
     * @param SearchCriteriaInterface $searchCriteria
     * @return SearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface;

    /**
     * Deletes slide
     *
     * @param SlideInterface $entity
     * @return void
     * @throws CouldNotDeleteException
     */
    public function delete(SlideInterface $entity): void;

    /**
     * Deletes slide by slide_id
     *
     * @param int $id
     * @return void
     * @throws NoSuchEntityException
     * @throws CouldNotDeleteException
     */
    public function deleteById($id): void;
}

Create  Model/SlideRepository.php file.

<?php

namespace Magelearn\Slider\Model;

use Exception;
use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Api\Data\SlideInterfaceFactory;
use Magelearn\Slider\Api\Data\SlideSearchResultsInterfaceFactory;
use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magelearn\Slider\Model\ResourceModel\Slide as SlideResource;
use Magelearn\Slider\Model\ResourceModel\Slide\CollectionFactory as SlideCollectionFactory;
use Magelearn\Slider\Model\Slide\FileInfo;
use Magelearn\Slider\Ui\Component\Slide\DataProvider;
use Magelearn\Slider\Model\ImageUploader;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;

class SlideRepository implements SlideRepositoryInterface
{
    /** @var SlideResource */
    private $slideResource;

    /** @var SlideInterfaceFactory */
    private $slideFactory;

    /** @var SlideCollectionFactory */
    private $slideCollectionFactory;

    /** @var SlideSearchResultsInterfaceFactory */
    private $searchResultsFactory;

    /** @var CollectionProcessorInterface */
    private $collectionProcessor;

    /** @var ImageUploader */
    private $imageUploader;

    /** @var FileInfo */
    private $fileInfo;

    /** @var SlideInterface[] */
    private $entityById = [];
    
    protected $logger;

    /**
     * @param SlideResource $slideResource
     * @param SlideInterfaceFactory $slideFactory
     * @param SlideCollectionFactory $slideCollectionFactory
     * @param SlideSearchResultsInterfaceFactory $searchResultsFactory
     * @param CollectionProcessorInterface $collectionProcessor
     * @param ImageUploader $imageUploader
     * @param FileInfo $fileInfo
     */
    public function __construct(
        SlideResource $slideResource,
        SlideInterfaceFactory $slideFactory,
        SlideCollectionFactory $slideCollectionFactory,
        SlideSearchResultsInterfaceFactory $searchResultsFactory,
        CollectionProcessorInterface $collectionProcessor,
        ImageUploader $imageUploader,
        FileInfo $fileInfo,
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->slideResource = $slideResource;
        $this->slideFactory = $slideFactory;
        $this->slideCollectionFactory  = $slideCollectionFactory;
        $this->searchResultsFactory = $searchResultsFactory;
        $this->collectionProcessor = $collectionProcessor;
        $this->imageUploader = $imageUploader;
        $this->fileInfo = $fileInfo;
        $this->logger = $logger;
    }

    /**
     * @inheritDoc
     */
    public function save(SlideInterface $entity): SlideInterface
    {
        try {
            $imagesData = $entity->getData();
            foreach (DataProvider::IMAGE_PARAMS as $imageParam) {
                $entity->setData($imageParam, null);
            }

            $this->slideResource->save($entity);

            $this->processImages($entity, $imagesData);
        } catch (Exception $e) {
            throw new CouldNotSaveException(__($e->getMessage()));
        }

        return $entity;
    }

    /**
     * @inheritDoc
     */
    public function getById($id): SlideInterface
    {
        if (isset($this->entityById[$id])) {
            return $this->entityById[$id];
        }

        $entity = $this->slideFactory->create();
        $this->slideResource->load($entity, $id);
        if (!$entity->getId()) {
            throw new NoSuchEntityException(__('The slide with the "%1" ID doesn\'t exist.', $id));
        }

        $this->entityById[$id] = $entity;

        return $entity;
    }

    /**
     * @inheritDoc
     */
    public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface
    {
        $searchResults = $this->searchResultsFactory->create();
        $searchResults->setSearchCriteria($searchCriteria);
        /** @var \Magelearn\Slider\Model\ResourceModel\Slide\Collection $collection */
        $collection = $this->slideCollectionFactory->create();

        $this->collectionProcessor->process($searchCriteria, $collection);

        $searchResults->setTotalCount($collection->getSize());
        $searchResults->setItems($collection->getItems());

        return $searchResults;
    }

    /**
     * @inheritDoc
     */
    public function delete(SlideInterface $entity): void
    {
        try {
            $this->slideResource->delete($entity);
        } catch (Exception $e) {
            throw new CouldNotDeleteException(__($e->getMessage()));
        }

        unset($this->entityById[$entity->getSlideId()]);
    }

    /**
     * @inheritDoc
     */
    public function deleteById($id): void
    {
        $entity = $this->getById($id);

        $this->delete($entity);
    }

    /**
     * @param SlideInterface|\Magelearn\Slider\Model\Slide $entity
     * @param array $data
     * @throws LocalizedException
     */
    private function processImages(SlideInterface $entity, array $data)
    {
        $imageUploaderBasePath = $this->imageUploader->getBasePath();
        foreach (DataProvider::IMAGE_PARAMS as $key) {
            if (isset($data[$key]) && (is_array($data[$key]) || is_string($data[$key]))) {
                $val = $data[$key];
                if (is_array($data[$key])) {
                    if (!empty($data[$key]['delete'])) {
                        $val = null;
                    } elseif (isset($data[$key][0]['name'], $data[$key][0]['tmp_name'])) {
                        $imagePrefix = $entity->getId() . '/' . $key;
                        $this->imageUploader->setBasePath($imageUploaderBasePath . '/' . $imagePrefix);

                        $image = $data[$key][0]['name'];
                        $image = $this->imageUploader->moveFileFromTmp($image);
                        $val = $imagePrefix . '/' . $image;

                        $this->imageUploader->setBasePath($imageUploaderBasePath);
                    } elseif (isset($data[$key][0]['url'])) {
                        $val = $this->fileInfo->getImagePathFromUrl($data[$key][0]['url']);
                    }
                }

                $entity->setData($key, $val);
            } else {
                $entity->setData($key, null);
            }
        }

        $this->slideResource->save($entity);
    }
}
Create Api/SliderManagementInterface.php file.
<?php

namespace Magelearn\Slider\Api;

use Magelearn\Slider\Api\Data\SlideInterface;

/**
 * @api
 */
interface SliderManagementInterface
{
    /**
     * @param int $sliderId
     * @param bool $onlyEnabled
     * @param bool $sorted
     *
     * @return SlideInterface[]
     */
    public function getSlides(int $sliderId, bool $onlyEnabled = false, bool $sorted = false): array;
}

Create Model/SliderManagement.php file.

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Model;

use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magelearn\Slider\Api\SliderManagementInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Api\SortOrderBuilder;

class SliderManagement implements SliderManagementInterface
{
    /**
     * @var SlideRepositoryInterface
     */
    private $slideRepository;

    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * @var SortOrderBuilder
     */
    private $sortOrderBuilder;

    public function __construct(
        SlideRepositoryInterface $slideRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        SortOrderBuilder $sortOrderBuilder
    ) {
        $this->slideRepository       = $slideRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->sortOrderBuilder      = $sortOrderBuilder;
    }

    /**
     * @inheritDoc
     */
    public function getSlides(int $sliderId, bool $onlyEnabled = false, bool $sorted = false): array
    {
        $this->searchCriteriaBuilder->addFilter(SlideInterface::SLIDER_ID, $sliderId);

        if ($onlyEnabled) {
            $this->searchCriteriaBuilder->addFilter(SlideInterface::IS_ACTIVE, 1);
            $this->searchCriteriaBuilder->addFilter(SlideInterface::STATUS, 1);
        }

        if ($sorted) {
            $sortOrder = $this->sortOrderBuilder->setField(SlideInterface::POSITION)->setAscendingDirection()->create();
            $this->searchCriteriaBuilder->addSortOrder($sortOrder);
        }

        $searchCriteria = $this->searchCriteriaBuilder->create();
        $slides         = $this->slideRepository->getList($searchCriteria);

        return $slides->getItems();
    }
}
Create Api/Data/SliderInterface.php file.
<?php

namespace Magelearn\Slider\Api\Data;

interface SliderInterface
{
    public const SLIDER_ID     = 'slider_id';
    public const TITLE         = 'title';
    public const STATUS        = 'status';
    public const CREATED_AT    = 'created_at';

    /**
     * @return int
     */
    public function getSliderId();

    /**
     * @param int $sliderId
     * @return SliderInterface
     */
    public function setSliderId($sliderId): self;

    /**
     * @return string
     */
    public function getTitle();

    /**
     * @param string $title
     * @return SliderInterface
     */
    public function setTitle($title): self;

    /**
     * @return bool
     */
    public function getStatus();

    /**
     * @param bool $status
     * @return SliderInterface
     */
    public function setStatus($status): self;
}

Create Model/Data/Slider.php file.

<?php

namespace Magelearn\Slider\Model\Data;

use Magelearn\Slider\Api\Data\SliderInterface;
use Magento\Framework\Model\AbstractModel;

class Slider extends AbstractModel implements SliderInterface
{
    /**
     * @return int
     */
    public function getSliderId()
    {
        return $this->_getData(self::SLIDER_ID);
    }

    /**
     * @param int $sliderId
     * @return SliderInterface
     */
    public function setSliderId($sliderId): SliderInterface
    {
        return $this->setData(self::SLIDER_ID, $sliderId);
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->_getData(self::TITLE);
    }

    /**
     * @param string $title
     * @return SliderInterface
     */
    public function setTitle($title): SliderInterface
    {
        return $this->setData(self::TITLE, $title);
    }

    /**
     * @return bool
     */
    public function getStatus()
    {
        return $this->_getData(self::STATUS);
    }

    /**
     * @param bool $status
     * @return SliderInterface
     */
    public function setStatus($status): SliderInterface
    {
        return $this->setData(self::STATUS, $status);
    }
}
Create Api/Data/SlideInterface.php file.
<?php

namespace Magelearn\Slider\Api\Data;

interface SlideInterface
{
    public const SLIDE_ID           = 'slide_id';
    public const SLIDER_ID          = 'slider_id';
    public const STATUS             = 'status';
    public const DATE_FROM          = 'date_from';
    public const DATE_TO            = 'date_to';
    public const IS_ACTIVE          = 'is_active';
    public const POSITION           = 'position';
    public const TITLE              = 'title';
    public const URL                = 'url';
    public const IMAGE_THUMBNAIL    = 'image_thumbnail';
    public const IMAGE_MOBILE       = 'image_mobile';
    public const IMAGE_SMALL        = 'image_small';
    public const IMAGE_MEDIUM       = 'image_medium';
    public const CREATED_AT         = 'created_at';
    
    public const MEDIA_SUBDIRECTORY_PATH = 'slider/images/upload/';
    
    public const VIDEO = 'video';
    public const VIDEO_CONTENT = 'video_content';
    public const CONTENT = 'content';
    
    public const IS_ACTIVE_COUNTDOWN = 'is_active_countdown';
    public const COUNTDOWN_DATE_FROM = 'countdown_date_from';
    public const COUNTDOWN_DATE_TO = 'countdown_date_to';
    public const COUNTDOWN_COLOR = 'countdown_color';
    public const COUNTDOWN_BACKGROUND_COLOR = 'countdown_background_color';
    
    public const SHOW_DAILY_DEAL = 'show_daily_deal';
    public const DAILY_DEAL_PRODUCT_ID   = 'daily_deal_product_id';
    public const DAILY_DEAL_COLOR = 'daily_deal_color';
    public const DAILY_DEAL_BACKGROUND_COLOR  = 'daily_deal_background_color';
    public const DAILY_DEAL_TOP = 'daily_deal_top';
    public const DAILY_DEAL_LEFT = 'daily_deal_left';

    /**
     * @return int
     */
    public function getSlideId();

    /**
     * @param int $slideId
     * @return SlideInterface
     */
    public function setSlideId($slideId): self;

    /**
     * @return int
     */
    public function getSliderId();

    /**
     * @param int $sliderId
     * @return SlideInterface
     */
    public function setSliderId($sliderId): self;

    /**
     * @return bool
     */
    public function getStatus();

    /**
     * @param bool $status
     * @return SlideInterface
     */
    public function setStatus($status): self;

    /**
     * @return string
     */
    public function getDateFrom();

    /**
     * @param string $date
     * @return SlideInterface
     */
    public function setDateFrom($date): self;

    /**
     * @return string
     */
    public function getDateTo();

    /**
     * @param string $date
     * @return SlideInterface
     */
    public function setDateTo($date): self;

    /**
     * @return bool
     */
    public function getIsActive();

    /**
     * @param bool $value
     * @return SlideInterface
     */
    public function setIsActive(bool $value): self;

    /**
     * @return int
     */
    public function getPosition();

    /**
     * @param int $position
     * @return SlideInterface
     */
    public function setPosition($position): self;

    /**
     * @return string
     */
    public function getTitle();

    /**
     * @param string $title
     * @return SlideInterface
     */
    public function setTitle($title): self;

    /**
     * @return string
     */
    public function getUrl();

    /**
     * @param string $url
     * @return SlideInterface
     */
    public function setUrl($url): self;

    /**
     * @return string
     */
    public function getImageThumbnail();

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageThumbnail($filename): self;

    /**
     * @return string
     */
    public function getImageMobile();

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageMobile($filename): self;

    /**
     * @return string
     */
    public function getImageSmall();

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageSmall($filename): self;

    /**
     * @return string
     */
    public function getImageMedium();

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageMedium($filename): self;
    
    /**
     * @return int
     */
    public function getVideo();
    
    /**
     * @param string $video
     * @return SlideInterface
     */
    public function setVideo(string $video): self;
    
    /**
     * @return mixed
     */
    public function getVideoContent();
    
    /**
     * @param string|null $videoContent
     *
     * @return $this
     */
    public function setVideoContent(?string $videoContent): self;
    
    /**
     * @return bool
     */
    public function isActiveCountdown();
    
    /**
     * @param bool $isActiveCountdown
     * @return SlideInterface
     */
    public function setIsActiveCountdown(bool $isActiveCountdown): self;
    
    /**
     * @return string
     */
    public function getContent();
    
    /**
     * @param string|null $content
     *
     * @return $this
     */
    public function setContent(?string $content): self;
    
    /**
     * @return string
     */
    public function getCountdownDateFrom();
    
    /**
     * @param string $countdownDateFrom
     *
     * @return SlideInterface
     */
    public function setCountdownDateFrom(string $countdownDateFrom);
    
    /**
     * @return string
     */
    public function getCountdownDateTo();
    
    /**
     * @param string $countdownDateTo
     *
     * @return SlideInterface
     */
    public function setCountdownDateTo(string $countdownDateTo);
    
    /**
     * @return string
     */
    public function getCountdownColor();
    
    /**
     * @param string $countdownColor
     *
     * @return SlideInterface
     */
    public function setCountdownColor(string $countdownColor);
    
    /**
     * @return string
     */
    public function getCountdownBackgroundColor();
    
    /**
     * @param string $countdownBackgroundColor
     *
     * @return SlideInterface
     */
    public function setCountdownBackgroundColor(string $countdownBackgroundColor);
    
    /**
     * @return bool
     */
    public function hasShowDailyDeal();
    
    /**
     * @param bool $status
     *
     * @return SlideInterface
     */
    public function setShowDailyDeal(bool $status);
    
    /**
     * @return string|null
     */
    public function getDailyDealProductId();
    
    /**
     * @param null|string $dailyDealId
     *
     * @return SlideInterface
     */
    public function setDailyDealProductId(?string $dailyDealId);
    
    /**
     * @return string
     */
    public function getDailyDealColor();
    
    /**
     * @param null|string $color
     *
     * @return SlideInterface
     */
    public function setDailyDealColor(?string $color);
    
    /**
     * @return string
     */
    public function getDailyDealBackgroundColor();
    
    /**
     * @param null|string $color
     *
     * @return SlideInterface
     */
    public function setDailyDealBackgroundColor(?string $color);
    
    /**
     * @return string
     */
    public function getDailyDealTop();
    
    /**
     * @param null|string $top
     *
     * @return SlideInterface
     */
    public function setDailyDealTop(?string $top);
    
    /**
     * @return string
     */
    public function getDailyDealLeft();
    
    /**
     * @param null|string $left
     *
     * @return SlideInterface
     */
    public function setDailyDealLeft(?string $left);
}

Create Model/Slide.php file.

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Model;

use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Model\ResourceModel\Slide as SlideResource;
use Magento\Framework\DataObject;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Event\ManagerInterface as EventManager;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;

class Slide extends AbstractModel implements SlideInterface
{
    /** @var EventManager */
    private EventManager $eventManager;
    
    /**
     * Slide constructor.
     *
     * @param Context               $context
     * @param Registry              $registry
     * @param EventManager          $eventManager
     * @param AbstractResource|null $resource
     * @param AbstractDb|null       $resourceCollection
     * @param array                 $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        EventManager $eventManager,
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null,
        array $data = []
        ) {
            parent::__construct($context, $registry, $resource, $resourceCollection, $data);
            
            $this->eventManager = $eventManager;
    }

    /**
     * @inheritDoc
     */
    public function _construct()
    {
        $this->_init(SlideResource::class);
    }

    /**
     * @return int
     */
    public function getSlideId()
    {
        return $this->_getData(self::SLIDE_ID);
    }

    /**
     * @param int $slideId
     * @return SlideInterface
     */
    public function setSlideId($slideId): SlideInterface
    {
        return $this->setData(self::SLIDE_ID, $slideId);
    }

    /**
     * @return int
     */
    public function getSliderId()
    {
        return $this->_getData(self::SLIDER_ID);
    }

    /**
     * @param int $sliderId
     * @return SlideInterface
     */
    public function setSliderId($sliderId): SlideInterface
    {
        return $this->setData(self::SLIDER_ID, $sliderId);
    }

    /**
     * @return bool
     */
    public function getStatus()
    {
        return $this->_getData(self::STATUS);
    }

    /**
     * @param bool $status
     * @return SlideInterface
     */
    public function setStatus($status): SlideInterface
    {
        return $this->setData(self::STATUS, $status);
    }

    /**
     * @return string
     */
    public function getDateFrom()
    {
        return $this->_getData(self::DATE_FROM);
    }

    /**
     * @param string $date
     * @return SlideInterface
     */
    public function setDateFrom($date): SlideInterface
    {
        return $this->setData(self::DATE_FROM, $date);
    }

    /**
     * @return string
     */
    public function getDateTo()
    {
        return $this->_getData(self::DATE_TO);
    }

    /**
     * @param string $date
     * @return SlideInterface
     */
    public function setDateTo($date): SlideInterface
    {
        return $this->setData(self::DATE_TO, $date);
    }

    /**
     * @inheritDoc
     */
    public function getIsActive(): bool
    {
        return (bool)$this->_getData(self::IS_ACTIVE);
    }

    /**
     * @inheritDoc
     */
    public function setIsActive(bool $value): SlideInterface
    {
        return $this->setData(self::IS_ACTIVE, $value);
    }

    /**
     * @return int
     */
    public function getPosition()
    {
        return $this->_getData(self::POSITION);
    }

    /**
     * @param int $position
     * @return SlideInterface
     */
    public function setPosition($position): SlideInterface
    {
        return $this->setData(self::POSITION, $position);
    }

    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->_getData(self::TITLE);
    }

    /**
     * @param string $title
     * @return SlideInterface
     */
    public function setTitle($title): SlideInterface
    {
        return $this->setData(self::TITLE, $title);
    }

    /**
     * @return string
     */
    public function getUrl()
    {
        return $this->_getData(self::URL);
    }

    /**
     * @param string $url
     * @return SlideInterface
     */
    public function setUrl($url): SlideInterface
    {
        return $this->setData(self::URL, $url);
    }

    /**
     * @return string
     */
    public function getImageThumbnail()
    {
        return $this->_getData(self::IMAGE_THUMBNAIL);
    }

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageThumbnail($filename): SlideInterface
    {
        return $this->setData(self::IMAGE_THUMBNAIL, $filename);
    }

    /**
     * @return string
     */
    public function getImageMobile()
    {
        return $this->_getData(self::IMAGE_MOBILE);
    }

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageMobile($filename): SlideInterface
    {
        return $this->setData(self::IMAGE_MOBILE, $filename);
    }

    /**
     * @return string
     */
    public function getImageSmall()
    {
        return $this->_getData(self::IMAGE_SMALL);
    }

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageSmall($filename): SlideInterface
    {
        return $this->setData(self::IMAGE_SMALL, $filename);
    }

    /**
     * @return string
     */
    public function getImageMedium()
    {
        return $this->_getData(self::IMAGE_MEDIUM);
    }

    /**
     * @param string $filename
     * @return SlideInterface
     */
    public function setImageMedium($filename): SlideInterface
    {
        return $this->setData(self::IMAGE_MEDIUM, $filename);
    }
    
    /** @inheritDoc */
    public function getVideo()
    {
        return (int) $this->_getData(self::VIDEO);
    }
    
    /** @inheritDoc */
    public function setVideo(string $video): SlideInterface
    {
        return $this->setData(self::VIDEO, $video);
    }
    
    /** @inheritDoc */
    public function getVideoContent()
    {
        return $this->_getData(self::VIDEO_CONTENT);
    }
    
    /** @inheritDoc */
    public function setVideoContent(?string $videoContent): SlideInterface
    {
        return $this->setData(self::VIDEO_CONTENT, $videoContent);
    }
    
    /** @inheritDoc */
    public function isActiveCountdown()
    {
        return (bool) $this->_getData(self::IS_ACTIVE_COUNTDOWN);
    }
    
    /** @inheritDoc */
    public function setIsActiveCountdown(bool $isActiveCountdown): SlideInterface
    {
        return $this->setData(self::IS_ACTIVE_COUNTDOWN, $isActiveCountdown);
    }
    
    /** @inheritDoc */
    public function getContent()
    {
        return $this->_getData(self::CONTENT);
    }
    
    /** @inheritDoc */
    public function setContent($content): SlideInterface
    {
        return $this->setData(self::CONTENT, $content);
    }
    
    /** @inheritDoc */
    public function getCountdownDateFrom()
    {
        return $this->_getData(self::COUNTDOWN_DATE_FROM);
    }
    
    /** @inheritDoc */
    public function setCountdownDateFrom(string $countdownDateFrom): SlideInterface
    {
        return $this->setData(self::COUNTDOWN_DATE_FROM, $countdownDateFrom);
    }
    
    /** @inheritDoc */
    public function getCountdownDateTo()
    {
        return $this->_getData(self::COUNTDOWN_DATE_TO);
    }
    
    /** @inheritDoc */
    public function setCountdownDateTo(string $countdownDateTo): SlideInterface
    {
        return $this->setData(self::COUNTDOWN_DATE_TO, $countdownDateTo);
    }
    
    /** @inheritDoc */
    public function getCountdownColor()
    {
        return $this->_getData(self::COUNTDOWN_COLOR);
    }
    
    /** @inheritDoc */
    public function setCountdownColor(string $countdownColor): SlideInterface
    {
        return $this->setData(self::COUNTDOWN_COLOR, $countdownColor);
    }
    
    /** @inheritDoc */
    public function getCountdownBackgroundColor()
    {
        return $this->_getData(self::COUNTDOWN_BACKGROUND_COLOR);
    }
    
    /** @inheritDoc */
    public function setCountdownBackgroundColor(string $countdownBackgroundColor): SlideInterface
    {
        return $this->setData(self::COUNTDOWN_BACKGROUND_COLOR, $countdownBackgroundColor);
    }
    
    /** @inheritDoc */
    public function hasShowDailyDeal()
    {
        return (bool) $this->_getData(self::SHOW_DAILY_DEAL);
    }
    
    /** @inheritDoc */
    public function setShowDailyDeal(bool $status): SlideInterface
    {
        return $this->setData(self::SHOW_DAILY_DEAL, $status);
    }
    
    /** @inheritDoc */
    public function getDailyDealProductId()
    {
        return $this->_getData(self::DAILY_DEAL_PRODUCT_ID);
    }
    
    /** @inheritDoc */
    public function setDailyDealProductId(?string $dailyDealId): SlideInterface
    {
        return $this->setData(self::DAILY_DEAL_PRODUCT_ID, $dailyDealId);
    }
    
    /** @inheritDoc */
    public function getDailyDealColor()
    {
        return $this->_getData(self::DAILY_DEAL_COLOR);
    }
    
    /** @inheritDoc */
    public function setDailyDealColor(?string $color): SlideInterface
    {
        return $this->setData(self::DAILY_DEAL_COLOR, $color);
    }
    
    /** @inheritDoc */
    public function getDailyDealBackgroundColor()
    {
        return $this->_getData(self::DAILY_DEAL_BACKGROUND_COLOR);
    }
    
    /** @inheritDoc */
    public function setDailyDealBackgroundColor(?string $color): SlideInterface
    {
        return $this->setData(self::DAILY_DEAL_BACKGROUND_COLOR, $color);
    }
    
    /** @inheritDoc */
    public function getDailyDealTop()
    {
        return $this->_getData(self::DAILY_DEAL_TOP);
    }
    
    /** @inheritDoc */
    public function setDailyDealTop(?string $top): SlideInterface
    {
        return $this->setData(self::DAILY_DEAL_TOP, $top);
    }
    
    /** @inheritDoc */
    public function getDailyDealLeft()
    {
        return $this->_getData(self::DAILY_DEAL_LEFT);
    }
    
    /** @inheritDoc */
    public function setDailyDealLeft(?string $left): SlideInterface
    {
        return $this->setData(self::DAILY_DEAL_LEFT, $left);
    }
    
    /** @inheritDoc */
    public function afterSave(): SlideInterface
    {
        if ($image = $this->getImageMedium()) {
            $filePath = new DataObject(['path' => self::MEDIA_SUBDIRECTORY_PATH . $image]);
            $this->eventManager->dispatch('magelearn_create_webp', ['magelearn_filepath' => $filePath]);
        }
        
        if ($image = $this->getImageMobile()) {
            $filePath = new DataObject(['path' => self::MEDIA_SUBDIRECTORY_PATH . $image]);
            $this->eventManager->dispatch('magelearn_create_webp', ['magelearn_filepath' => $filePath]);
        }

        return parent::afterSave();
    }
}
Create Api/Data/SliderSearchResultsInterface.php file.
<?php

namespace Magelearn\Slider\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface SliderSearchResultsInterface extends SearchResultsInterface
{
    /**
     * Get sliders list.
     *
     * @return \Magelearn\Slider\Api\Data\SliderInterface[]
     */
    public function getItems();

    /**
     * Set sliders list.
     *
     * @param \Magelearn\Slider\Api\Data\SliderInterface[] $items
     * @return $this
     */
    public function setItems(array $items);
}

Create Api/Data/SlideSearchResultsInterface.php file.

<?php

namespace Magelearn\Slider\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface SlideSearchResultsInterface extends SearchResultsInterface
{
    /**
     * Get slides list.
     *
     * @return \Magelearn\Slider\Api\Data\SlideInterface[]
     */
    public function getItems();

    /**
     * Set slides list.
     *
     * @param \Magelearn\Slider\Api\Data\SlideInterface[] $items
     * @return $this
     */
    public function setItems(array $items);
}

Create Model/ResourceModel/Slide.php file.

<?php

namespace Magelearn\Slider\Model\ResourceModel;

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

/**
 * Class Slide
 * @package Magelearn\Slider\Model\ResourceModel
 */
class Slide extends AbstractDb
{
    protected function _construct()
    {
        $this->_init('magelearn_slider_slider_item', 'slide_id');
    }
}

Create Model/ResourceModel/Slider.php file.

<?php

namespace Magelearn\Slider\Model\ResourceModel;

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

/**
 * Class Slider
 * @package Magelearn\Slider\Model\ResourceModel
 */
class Slider extends AbstractDb
{
    protected function _construct()
    {
        $this->_init('magelearn_slider_slider', 'slider_id');
    }
}

Create Model/ResourceModel/Slide/Collection.php file.

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Model\ResourceModel\Slide;

use Magelearn\Slider\Model\ResourceModel\Slide as SlideResource;
use Magelearn\Slider\Model\Slide;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

class Collection extends AbstractCollection
{
    /**
     * @inheritDoc
     */
    protected function _construct()
    {
        $this->_init(Slide::class, SlideResource::class);
        $this->_setIdFieldName('slide_id');
    }
}

Create Model/ResourceModel/Slider/Collection.php file.

<?php

namespace Magelearn\Slider\Model\ResourceModel\Slider;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

/**
 * Class Collection
 * @package Magelearn\Slider\Model\ResourceModel\Slider
 */
class Collection extends AbstractCollection
{
    /**
     * @var string
     */
    protected $_idFieldName = 'slider_id';

    protected function _construct()
    {
        $this->_init(\Magelearn\Slider\Model\Slider::class, \Magelearn\Slider\Model\ResourceModel\Slider::class);
    }

    /** switch to data models */
    protected final function _afterLoad()
    {
        parent::_afterLoad();

        $anemicItems = [];

        /** @var \Magelearn\Slider\Model\Slider $item */
        foreach ($this->_items as $item) {
            $anemicItems[] = $item->getDataModel();
        }

        $this->_items = $anemicItems;

        return $this;
    }
}

Now as per the highlighted code above add file Model/Slider.php

<?php

namespace Magelearn\Slider\Model;

use Magelearn\Slider\Api\Data\SliderInterface;
use Magelearn\Slider\Api\Data\SliderInterfaceFactory;
use Magento\Framework\Api\AttributeValueFactory;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Api\ExtensionAttributesFactory;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\AbstractExtensibleModel;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Reflection\DataObjectProcessor;
use Magento\Framework\Registry;
use Magelearn\Slider\Model\ResourceModel\Slider as SliderResource;

/**
 * Class Slide
 * @package Magelearn\Slider\Model
 */
class Slider extends AbstractExtensibleModel
{
    /**
     * @var DataObjectHelper
     */
    private $dataObjectHelper;

    /**
     * @var DataObjectProcessor
     */
    private $dataObjectProcessor;

    /**
     * @var SliderInterfaceFactory
     */
    private $sliderFactory;

    /**
     * Slide constructor.
     *
     * @param Context $context
     * @param Registry $registry
     * @param SlideInterfaceFactory $slideFactory
     * @param DataObjectHelper $dataObjectHelper
     * @param DataObjectProcessor $dataObjectProcessor
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        SliderInterfaceFactory $sliderFactory,
        DataObjectHelper $dataObjectHelper,
        DataObjectProcessor $dataObjectProcessor,
        ExtensionAttributesFactory $extensionFactory,
        AttributeValueFactory $customAttributeFactory,
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        parent::__construct($context, $registry, $extensionFactory, $customAttributeFactory, $resource, $resourceCollection, $data);

        $this->sliderFactory       = $sliderFactory;
        $this->dataObjectHelper    = $dataObjectHelper;
        $this->dataObjectProcessor = $dataObjectProcessor;

        $this->_init(SliderResource::class); // yes, this works fine

        // if ANY of You have problem with line below please refer to deprecated _getResource,
        // cause either You use deprecated methods or set Resource here, thank You.
        $this->_resource === null && $this->_resource = ObjectManager::getInstance()->get($this->_resourceName);
    }

    /**
     * @return mixed
     */
    public function getDataModel()
    {
        $slideAnemicModel = $this->sliderFactory->create();
        $this->dataObjectHelper->populateWithArray(
            $slideAnemicModel,
            $this->getData(),
            SliderInterface::class
        );

        $this->cleanup();

        return $slideAnemicModel;
    }

    /**
     * @param int $modelId
     * @param null $field
     * @return SliderInterface
     */
    public final function load($modelId, $field = null)
    {
        $this->cleanup();

        $this->_resource->load($this, $modelId, $field);

        return $this->getDataModel();
    }

    /**
     * @param SliderInterface $slide
     * @return SliderInterface
     * @throws \Exception
     */
    private function saveAnemicModel(SliderInterface $slider = null)
    {
        $this->cleanup();
        $this->_data = $this->dataObjectProcessor->buildOutputDataArray($slider, SliderInterface::class);
        $this->_hasDataChanges = true;
        $this->_resource->save($this);
        $slider->setSliderId($this->getId());
        $this->cleanup();
        return $slider;
    }

    /**
     * Safety wrapper
     *
     * @param SliderInterface|null $slide
     * @return SliderInterface|AbstractModel
     * @throws \Exception
     */
    public final function save(SliderInterface $slider = null)
    {
        if (!$slider instanceof SliderInterface) {
            throw new \RuntimeException('[HAL 9000] I’m sorry Dave, I’m afraid I can’t do that');
        }

        return $this->saveAnemicModel($slider);
    }

    private function cleanup()
    {
        // break reference the HARD way
        unset($this->_data);
        $this->_data = [];
    }

    /**
     * @param SliderInterface|null $slide
     * @return void
     * @throws \Exception
     */
    public final function delete(SliderInterface $slider = null)
    {
        if (!$slider instanceof SliderInterface) {
            throw new \RuntimeException('[HAL 9000] I’m sorry Dave, I’m afraid I can’t do that');
        }

        $this->setId($slider->getSliderId())->isDeleted(false);
        $this->_resource->delete($this);
    }
}

Now as per defined in etc/di.xml file, we will add image uploader class.

Add file at Model/ImageUploader.php

<?php

namespace Magelearn\Slider\Model;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
use Magento\MediaStorage\Helper\File\Storage\Database;
use Magento\MediaStorage\Model\File\UploaderFactory;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;

/**
 * Magelearn Slider image uploader
 */
class ImageUploader
{
    /**
     * Core file storage database
     *
     * @var Database
     */
    private $coreFileStorageDatabase;

    /**
     * Media directory object (writable).
     *
     * @var WriteInterface
     */
    private $mediaDirectory;

    /**
     * Uploader factory
     *
     * @var UploaderFactory
     */
    private $uploaderFactory;

    /**
     * Store manager
     *
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * Base tmp path
     *
     * @var string
     */
    public $baseTmpPath;

    /**
     * Base path
     *
     * @var string
     */
    public $basePath;

    /**
     * Allowed extensions
     *
     * @var string
     */
    public $allowedExtensions;

    /**
     * ImageUploader constructor.
     * @param Database $coreFileStorageDatabase
     * @param Filesystem $filesystem
     * @param UploaderFactory $uploaderFactory
     * @param StoreManagerInterface $storeManager
     * @param LoggerInterface $logger
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    public function __construct(
        Database $coreFileStorageDatabase,
        Filesystem $filesystem,
        UploaderFactory $uploaderFactory,
        StoreManagerInterface $storeManager,
        LoggerInterface $logger
    ) {
        $this->coreFileStorageDatabase = $coreFileStorageDatabase;
        $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
        $this->uploaderFactory = $uploaderFactory;
        $this->storeManager = $storeManager;
        $this->logger = $logger;
        $this->baseTmpPath = "slider/tmp/images";
        $this->basePath = "slider/images/upload";
        $this->allowedExtensions= ['jpg', 'jpeg', 'gif', 'png', 'svg'];
    }

    /**
     * Set base tmp path
     *
     * @param string $baseTmpPath
     *
     * @return void
     */
    public function setBaseTmpPath($baseTmpPath)
    {
        $this->baseTmpPath = $baseTmpPath;
    }

    /**
     * Set base path
     *
     * @param string $basePath
     *
     * @return void
     */
    public function setBasePath($basePath)
    {
        $this->basePath = $basePath;
    }

    /**
     * Set allowed extensions
     *
     * @param string[] $allowedExtensions
     *
     * @return void
     */
    public function setAllowedExtensions($allowedExtensions)
    {
        $this->allowedExtensions = $allowedExtensions;
    }

    /**
     * Retrieve base tmp path
     *
     * @return string
     */
    public function getBaseTmpPath()
    {
        return $this->baseTmpPath;
    }

    /**
     * Retrieve base path
     *
     * @return string
     */
    public function getBasePath()
    {
        return $this->basePath;
    }

    /**
     * Retrieve base path
     *
     * @return string[]
     */
    public function getAllowedExtensions()
    {
        return $this->allowedExtensions;
    }

    /**
     * Retrieve path
     *
     * @param string $path
     * @param string $imageName
     *
     * @return string
     */
    public function getFilePath($path, $imageName)
    {
        return rtrim($path, '/') . '/' . ltrim($imageName, '/');
    }

    /**
     * Checking file for moving and move it
     *
     * @param string $imageName
     *
     * @return string
     *
     * @throws LocalizedException
     */
    public function moveFileFromTmp($imageName)
    {
        $baseTmpPath = $this->getBaseTmpPath();
        $basePath = $this->getBasePath();

        $baseImagePath = $this->getFilePath($basePath, $imageName);
        $baseTmpImagePath = $this->getFilePath($baseTmpPath, $imageName);

        try {
            $this->coreFileStorageDatabase->copyFile(
                $baseTmpImagePath,
                $baseImagePath
            );
            $this->mediaDirectory->renameFile(
                $baseTmpImagePath,
                $baseImagePath
            );
        } catch (\Exception $e) {
            throw new LocalizedException(
                __('Something went wrong while saving the file(s).')
            );
        }

        return $imageName;
    }

    /**
     * Checking file for save and save it to tmp dir
     *
     * @param string $fileId
     *
     * @return string[]
     *
     * @throws LocalizedException
     */
    public function saveFileToTmpDir($fileId)
    {
        $baseTmpPath = $this->getBaseTmpPath();

        $uploader = $this->uploaderFactory->create(['fileId' => $fileId]);
        $uploader->setAllowedExtensions($this->getAllowedExtensions());
        $uploader->setAllowRenameFiles(true);

        $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath));

        if (!$result) {
            throw new LocalizedException(
                __('File can not be saved to the destination folder.')
            );
        }

        /**
         * Workaround for prototype 1.7 methods "isJSON", "evalJSON" on Windows OS
         */
        $result['tmp_name'] = str_replace('\\', '/', $result['tmp_name']);
        $result['path'] = str_replace('\\', '/', $result['path']);
        $result['url'] = $this->storeManager
                ->getStore()
                ->getBaseUrl(
                    \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
                ) . $this->getFilePath($baseTmpPath, $result['file']);
        $result['name'] = $result['file'];

        if (isset($result['file'])) {
            try {
                $relativePath = rtrim($baseTmpPath, '/') . '/' . ltrim($result['file'], '/');
                $this->coreFileStorageDatabase->saveFile($relativePath);
            } catch (\Exception $e) {
                $this->logger->critical($e);
                throw new LocalizedException(
                    __('Something went wrong while saving the file(s).')
                );
            }
        }

        return $result;
    }
}

Add file at Controller/Adminhtml/Slide/Image/Upload.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Controller\Adminhtml\Slide\Image;

use Exception;
use Magento\Backend\App\Action;
use Magento\Framework\Registry;
use Magento\Backend\App\Action\Context;
use Magelearn\Slider\Model\ImageUploader;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\Controller\Result\JsonFactory;

class Upload extends Action implements HttpPostActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var ImageUploader
     */
    private $imageUploader;

    /**
     * @var JsonFactory
     */
    private $resultJsonFactory;
    
    protected $logger;

    /**
     * @param Context $context
     * @param \Magento\Framework\Registry $coreRegistry
     * @param ImageUploader $imageUploader
     * @param JsonFactory $resultJsonFactory
     */
    public function __construct(
        Context $context,
        Registry $coreRegistry,
        ImageUploader $imageUploader,
        JsonFactory $resultJsonFactory,
        \Psr\Log\LoggerInterface $logger
    ) {
        parent::__construct($context, $coreRegistry);
        $this->imageUploader = $imageUploader;
        $this->resultJsonFactory = $resultJsonFactory;
        $this->logger = $logger;
    }

    /**
     * @inheritDoc
     */
    public function execute()
    {
        $imageId = $this->_request->getParam('param_name', 'image');

        try {
            $result = $this->imageUploader->saveFileToTmpDir($imageId);

            $result['cookie'] = [
                'name' => $this->_getSession()->getName(),
                'value' => $this->_getSession()->getSessionId(),
                'lifetime' => $this->_getSession()->getCookieLifetime(),
                'path' => $this->_getSession()->getCookiePath(),
                'domain' => $this->_getSession()->getCookieDomain(),
            ];
        } catch (Exception $e) {
            $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
        }

        return $this->resultJsonFactory->create()->setData($result);
    }
}

Now as per defined in etc/adminhtml/menu.xml and etc/adminhtml/routes.xml file, we will add our Layout and controller files for slider and slides.

Add controller file at Controller/Adminhtml/Slider/Index.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\View\Result\PageFactory;

/**
 * Class Index
 * @package Magelearn\Slider\Controller\Adminhtml\Slider
 */
class Index extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var PageFactory
     */
    private $resultPageFactory;

    /**
     * Index constructor.
     * @param Context $context
     * @param PageFactory $pageFactory
     */
    public function __construct(Context $context, PageFactory $pageFactory)
    {
        parent::__construct($context);
        $this->resultPageFactory = $pageFactory;
    }

    /**
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('Magento_Backend::content_elements');
        $resultPage->getConfig()->getTitle()->prepend(__('Sliders'));
        return $resultPage;
    }
}

Add view/adminhtml/layout/slider_slider_index.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="slider_slider_listing"/>
        </referenceContainer>
    </body>
</page>

As per highlighted in above code add file at view/adminhtml/ui_component/slider_slider_listing.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">slider_slider_listing.slider_grid_data_source</item>
            <item name="deps" xsi:type="string">slider_slider_listing.slider_grid_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">slider_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="array">
                <item name="name" xsi:type="string">add</item>
                <item name="label" xsi:type="string" translate="true">Add New Slider</item>
                <item name="class" xsi:type="string">primary</item>
                <item name="url" xsi:type="string">*/*/new</item>
            </item>
            <item name="clearCache" xsi:type="array">
                <item name="name" xsi:type="string">clearCache</item>
                <item name="label" xsi:type="string" translate="true">Clear homepage cache</item>
                <item name="class" xsi:type="string">secondary</item>
                <item name="url" xsi:type="string">*/*/clearCache</item>
            </item>
        </item>
    </argument>
    <dataSource name="slider_grid_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider</argument>
            <argument name="name" xsi:type="string">slider_grid_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">slider_id</argument>
            <argument name="requestFieldName" xsi:type="string">slider_id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
                    <item name="update_url" xsi:type="url" path="mui/index/render"/>
                    <item name="storageConfig" xsi:type="array">
                        <item name="indexField" xsi:type="string">slider_id</item>
                    </item>
                </item>
            </argument>
        </argument>
    </dataSource>

    <listingToolbar name="listing_top">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="sticky" xsi:type="boolean">true</item>
            </item>
        </argument>
        <filters name="listing_filters"/>
        <paging name="listing_paging"/>
        <container name="columns_controls">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="columnsData" xsi:type="array">
                        <item name="provider" xsi:type="string">slider_slider_listing.slider_slider_listing.slider_columns</item>
                    </item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
                    <item name="displayArea" xsi:type="string">dataGridActions</item>
                </item>
            </argument>
        </container>
        <massaction name="listing_massaction">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="selectProvider" xsi:type="string">slider_slider_listing.slider_slider_listing.slider_columns.ids</item>
                    <item name="displayArea" xsi:type="string">bottom</item>
                    <item name="indexField" xsi:type="string">slider_id</item>
                </item>
            </argument>
            <action name="delete">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="type" xsi:type="string">delete</item>
                        <item name="label" xsi:type="string" translate="true">Delete</item>
                        <item name="url" xsi:type="url" path="slider/slider/massDelete"/>
                        <item name="confirm" xsi:type="array">
                            <item name="title" xsi:type="string" translate="true">Delete items</item>
                            <item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
                        </item>
                    </item>
                </argument>
            </action>
        </massaction>
    </listingToolbar>

    <columns name="slider_columns">
        <selectionsColumn name="ids">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">slider_id</item>
                </item>
            </argument>
        </selectionsColumn>

        <column name="slider_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">textRange</item>
                    <item name="sorting" xsi:type="string">asc</item>
                    <item name="label" xsi:type="string" translate="true">ID</item>
                </item>
            </argument>
        </column>

        <column name="title">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Title</item>
                </item>
            </argument>
        </column>

        <column name="status">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magento\Config\Model\Config\Source\Yesno</item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
                    <item name="label" xsi:type="string" translate="true">Enabled</item>
                </item>
            </argument>
        </column>

        <actionsColumn name="actions" class="Magelearn\Slider\Ui\Component\Slider\Grid\Columns\Actions">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">slider_id</item>
                    <item name="sortOrder" xsi:type="number">200</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>
</listing>

Now we will provide action files for slider listing xml.

Add file at Controller/Adminhtml/Slider/MassDelete.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Exception;
use Magelearn\Slider\Api\SliderRepositoryInterface;
use Magelearn\Slider\Model\ResourceModel\Slider\CollectionFactory;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Ui\Component\MassAction\Filter;

class MassDelete extends Action implements HttpPostActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var SliderRepositoryInterface
     */
    private $sliderRepository;

    /**
     * @var CollectionFactory
     */
    private $sliderCollectionFactory;

    /**
     * @var Filter
     */
    private $filter;

    /**
     * @param Context $context
     * @param SliderRepositoryInterface $sliderRepository
     * @param CollectionFactory $sliderCollectionFactory
     * @param Filter $filter
     */
    public function __construct(
        Context $context,
        SliderRepositoryInterface $sliderRepository,
        CollectionFactory $sliderCollectionFactory,
        Filter $filter
    ) {
        parent::__construct($context);
        $this->sliderRepository = $sliderRepository;
        $this->sliderCollectionFactory = $sliderCollectionFactory;
        $this->filter = $filter;
    }

    /**
     * @inheritDoc
     */
    public function execute()
    {
        try {
            $slidersCollectionSize = $this->removeElements();
            $this->messageManager->addSuccessMessage(
                __('A total of %1 slider(s) have been deleted.', $slidersCollectionSize)
            );
        } catch (Exception $e) {
            $this->messageManager->addErrorMessage(__('Something went wrong: %1', $e->getMessage()));
        }

        return $this->_redirect('*/*/');
    }


    /**
     * @return int
     * @throws LocalizedException
     */
    private function removeElements(): int
    {
        $slidersCollection = $this->filter->getCollection($this->sliderCollectionFactory->create());
        $slidersCollectionSize = $slidersCollection->getSize();

        foreach ($slidersCollection as $sliderAnemicModel) {
            $this->sliderRepository->delete($sliderAnemicModel);
        }

        return $slidersCollectionSize;
    }
}

Also add Controller/Adminhtml/Slider/ClearCache.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Magelearn\Slider\Block\Slider;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Backend\Model\View\Result\ForwardFactory;
use Magento\Cms\Helper\Page;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\View\Result\PageFactory;
use Magento\PageCache\Model\Cache\Type;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
 * Class Index
 * @package Magelearn\Slider\Controller\Adminhtml\Slider
 */
class ClearCache extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var Type
     */
    private $fullPageCache;

    /**
     * @var mixed
     */
    private $scopeConfig;

    /**
     * Index constructor.
     * @param Context $context
     * @param PageFactory $pageFactory
     */
    public function __construct(
        Context $context,
        StoreManagerInterface $storeManager,
        ScopeConfigInterface $scopeConfig = null
    ) {
        parent::__construct($context);
        $this->scopeConfig  = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
        $this->storeManager = $storeManager;
    }

    public function execute()
    {
        // clean all sliders cache
        $this->getCache()->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [Slider::CACHE_TAG]);
        $this->messageManager->addSuccessMessage(__('All sliders cache cleared'));

        foreach ($this->storeManager->getStores() as $store) {
            $storeHomepageId = $this->scopeConfig->getValue(Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE,
                $store->getCode());
            $this->getCache()->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG,
                [\Magento\Cms\Model\Page::CACHE_TAG . '_' . $storeHomepageId]);
            $this->messageManager->addSuccessMessage(__('Homepage cache cleared for %1', $store->getCode()));
        }

        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        return $resultRedirect->setPath('slider/*/');
    }

    private function getCache()
    {
        if (!$this->fullPageCache) {
            $this->fullPageCache = ObjectManager::getInstance()->get(Type::class);
        }
        return $this->fullPageCache;
    }
}

Add file at Ui/Component/Slider/Grid/Columns/Actions.php

<?php

namespace Magelearn\Slider\Ui\Component\Slider\Grid\Columns;

use Magento\Ui\Component\Listing\Columns\Column;

/**
 * Class Actions
 * @package Magelearn\Slider\Ui\Component\Slider\Grid\Columns
 */
class Actions extends Column
{
    public const SLIDER_ID       = 'slider_id';
    public const URL_PATH_EDIT   = 'slider/slider/edit';
    public const URL_PATH_DELETE = 'slider/slider/delete';
    public const URL_PATH_SLIDES = 'slider/slide';

    /**
     * @param array $dataSource
     * @return array
     */
    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as &$item) {
                $name = $this->getData('name');
                if (isset($item[self::SLIDER_ID])) {
                    $item[$name]['edit']   = [
                        'href' => $this->context->getUrl(self::URL_PATH_EDIT,
                            [self::SLIDER_ID => $item[self::SLIDER_ID]]),
                        'label' => __('Edit'),
                        'hidden' => false,
                    ];
                    $item[$name]['slides'] = [
                        'href' => $this->context->getUrl(self::URL_PATH_SLIDES,
                            [self::SLIDER_ID => $item[self::SLIDER_ID]]),
                        'label' => __('Slides'),
                        'hidden' => false,
                    ];
                    $item[$name]['delete'] = [
                        'href' => $this->context->getUrl(self::URL_PATH_DELETE,
                            [self::SLIDER_ID => $item[self::SLIDER_ID]]),
                        'label' => __('Delete'),
                        'hidden' => false,
                    ];
                }
            }
        }

        return $dataSource;
    }
}
Also add Controller/Adminhtml/Slider/Delete.php file.
<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Magelearn\Slider\Api\SliderRepositoryInterface;
use Magelearn\Slider\Model\SliderFactory;
use Magento\Backend\App\{Action, Action\Context};

/**
 * Class Delete
 * @package Magelearn\Slider\Controller\Adminhtml\Slider
 */
class Delete extends Action
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var SliderRepositoryInterface
     */
    private $sliderRepository;

    /**
     * Delete constructor.
     * @param Context $context
     * @param SliderRepositoryInterface $sliderRepository
     */
    public function __construct(
        Context $context,
        SliderRepositoryInterface $sliderRepository
    ) {
        parent::__construct($context);
        $this->sliderRepository = $sliderRepository;
    }

    /**
     * @param $id
     */
    protected function removeSlider($id)
    {
        try {
            $this->sliderRepository->deleteById($id);
        } catch (\Exception $e) {
            $this->messageManager->addErrorMessage($e->getMessage());
        }
    }

    public function execute()
    {
        $id = $this->getRequest()->getParam('slider_id');
        $this->removeSlider($id);
        $this->messageManager->addSuccessMessage(__('Slider has been deleted.'));

        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();
        return $resultRedirect->setPath('slider/*/');
    }
}

Now add Controller/Adminhtml/Slider/NewAction.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Backend\Model\View\Result\ForwardFactory;

/**
 * Class NewAction
 * @package Magelearn\Slider\Controller\Adminhtml\Slider
 */
class NewAction extends Action
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var ForwardFactory
     */
    private $resultForwardFactory;

    /**
     * NewAction constructor.
     * @param Context $context
     * @param ForwardFactory $resultForwardFactory
     */
    public function __construct(
        Context $context,
        ForwardFactory $resultForwardFactory
    ) {
        $this->resultForwardFactory = $resultForwardFactory;
        parent::__construct($context);
    }

    /**
     * @return mixed
     */
    public function execute()
    {
        $resultForward = $this->resultForwardFactory->create();

        return $resultForward->forward('edit');
    }
}
Now as per the highlighted code above, this new action is redirected to the Edit action.

Add file at Controller/Adminhtml/Slider/Edit.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Magelearn\Slider\Api\Data\SliderInterfaceFactory;
use Magelearn\Slider\Api\SliderRepositoryInterface;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Registry;
use Magento\Framework\View\Result\PageFactory;

/**
 * Class Edit
 * @package Magelearn\Slider\Controller\Adminhtml\Slider
 */
class Edit extends Action
{

    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var SliderInterfaceFactory
     */
    private $sliderRepository;
    /**
     * @var PageFactory
     */
    private $resultPageFactory;
    /**
     * @var Registry
     */
    private $coreRegistry;

    /**
     * @var SliderInterfaceFactory
     */
    private $sliderFactory;

    /**
     * Edit constructor.
     * @param Context $context
     * @param SliderRepositoryInterface $sliderRepository
     * @param SliderInterfaceFactory $sliderFactory
     * @param Registry $registry
     * @param PageFactory $pageFactory
     */
    public function __construct(
        Context $context,
        SliderRepositoryInterface $sliderRepository,
        SliderInterfaceFactory $sliderFactory,
        Registry $registry,
        PageFactory $pageFactory
    ) {
        parent::__construct($context);
        $this->sliderRepository  = $sliderRepository;
        $this->coreRegistry      = $registry;
        $this->resultPageFactory = $pageFactory;
        $this->sliderFactory     = $sliderFactory;
    }

    /**
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {

        $id = $this->getRequest()->getParam('slider_id');
        if ($id) {
            $sliderAnemicModel = $this->sliderRepository->getById($id);
        } else {
            $sliderAnemicModel = $this->sliderFactory->create();
        }
        $resultPage = $this->resultPageFactory->create();
        $resultPage->getConfig()->getTitle()->prepend($sliderAnemicModel->getSliderId() ? $sliderAnemicModel->getTitle() : __('Add new slider'));

        return $resultPage;
    }
}

Add layout file at view/adminhtml/layout/slider_slider_edit.xml 

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="styles"/>
    <update handle="editor"/>
    <body>
        <referenceContainer name="content">
            <uiComponent name="slider_slider_form"/>
        </referenceContainer>
    </body>
</page>

As per Highlighted code above add file view/adminhtml/ui_component/slider_slider_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">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">slider_slider_form.slider_form_data_source</item>
            <item name="deps" xsi:type="string">slider_slider_form.slider_form_data_source</item>
        </item>
        <item name="label" xsi:type="string" translate="true">Slider Form</item>
        <item name="config" xsi:type="array">
            <item name="dataScope" xsi:type="string">data</item>
            <item name="namespace" xsi:type="string">slider_form</item>
        </item>
        <item name="template" xsi:type="string">templates/form/collapsible</item>
        <item name="buttons" xsi:type="array">
            <item name="back" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slider\Edit\Back</item>
            <item name="reset" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slider\Edit\Reset</item>
            <item name="delete" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slider\Edit\Delete</item>
            <item name="save" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slider\Edit\Save</item>
            <item name="save_and_continue" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slider\Edit\SaveAndContinueEdit</item>
        </item>
    </argument>
    <dataSource name="slider_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Magelearn\Slider\Model\ResourceModel\Slider\DataProvider</argument>
            <argument name="name" xsi:type="string">slider_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">slider_id</argument>
            <argument name="requestFieldName" xsi:type="string">slider_id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="submit_url" xsi:type="url" path="*/*/save"/>
                </item>
            </argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            </item>
        </argument>
    </dataSource>

    <fieldset name="general">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">General Information</item>
                <item name="sortOrder" xsi:type="number">10</item>
                <item name="collapsible" xsi:type="boolean">false</item>
            </item>
        </argument>

        <field name="slider_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">false</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">slider_id</item>
                </item>
            </argument>
        </field>

        <field name="title">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Title</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="sortOrder" xsi:type="number">0</item>
                    <item name="source" xsi:type="string">title</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>

        <field name="status">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magelearn\Slider\Ui\Component\Form\Field\Status</item>
                <item name="config" xsi:type="array">
                    <item name="sortOrder" xsi:type="number">40</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Status</item>
                    <item name="formElement" xsi:type="string">select</item>
                    <item name="source" xsi:type="string">status</item>
                    <item name="dataScope" xsi:type="string">status</item>
                    <item name="default" xsi:type="string">1</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>
    </fieldset>
</form>

As per highlighrted code above we will first add our DataProvider file at Magelearn/Slider/Model/ResourceModel/Slider/DataProvider.php

<?php

namespace Magelearn\Slider\Model\ResourceModel\Slider;

use Magelearn\Slider\Api\Data\SliderInterface;
use Magelearn\Slider\Model\ResourceModel\Slider\CollectionFactory;
use Magento\{Store\Model\StoreManagerInterface, Ui\DataProvider\AbstractDataProvider};
use Magento\Framework\Reflection\DataObjectProcessor;

/**
 * Class DataProvider
 * @package Magelearn\Slider\Model\ResourceModel\Slider
 */
class DataProvider extends AbstractDataProvider
{
    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var DataObjectProcessor
     */
    private $dataObjectProcessor;

    /**
     * DataProvider constructor.
     * @param string $name
     * @param string $primaryFieldName
     * @param string $requestFieldName
     * @param StoreManagerInterface $storeManager
     * @param CollectionFactory $collectionFactory
     * @param array $meta
     * @param array $data
     */
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        StoreManagerInterface $storeManager,
        CollectionFactory $collectionFactory,
        DataObjectProcessor $dataObjectProcessor,
        array $meta = [],
        array $data = []
    ) {
        $this->collection          = $collectionFactory->create();
        $this->storeManager        = $storeManager;
        $this->dataObjectProcessor = $dataObjectProcessor;
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
    }

    /**
     * @return array
     */
    public function getData()
    {
        if (isset($this->loadedData)) {
            return $this->loadedData;
        }

        $items            = $this->collection->getItems();
        $this->loadedData = array();

        foreach ($items as $item) {
            $itemData                               = $this->dataObjectProcessor->buildOutputDataArray($item,
                SliderInterface::class);
            $this->loadedData[$item->getSliderId()] = $itemData;
        }

        return $this->loadedData;
    }
}
And last we will add Controller/Adminhtml/Slider/Save.php file.
<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slider;

use Magelearn\Slider\Api\Data\SliderInterface;
use Magelearn\Slider\Api\Data\SliderInterfaceFactory;
use Magelearn\Slider\Api\SliderRepositoryInterface;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\Model\Exception;
use Magento\Store\Model\StoreManagerInterface;

/**
 * Class Save
 * @package Magelearn\Slider\Controller\Adminhtml\Slider
 */
class Save extends Action implements HttpPostActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slider';

    /**
     * @var SliderFactory
     */
    private $sliderFactory;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var SliderRepositoryInterface
     */
    private $sliderRepository;

    /**
     * @var DataObjectHelper
     */
    private $dataObjectHelper;

    /**
     * Save constructor.
     * @param Context $context
     * @param SliderInterfaceFactory\ $sliderAnemicModelFactory
     * @param StoreManagerInterface $storeManager
     */
    public function __construct(
        Context $context,
        SliderRepositoryInterface $sliderRepository,
        SliderInterfaceFactory $sliderFactory,
        DataObjectHelper $dataObjectHelper,
        StoreManagerInterface $storeManager
    ) {
        $this->sliderRepository = $sliderRepository;
        $this->sliderFactory    = $sliderFactory;
        $this->dataObjectHelper = $dataObjectHelper;
        $this->storeManager     = $storeManager;
        parent::__construct($context);
    }


    public function execute()
    {
        $data = $this->getRequest()->getPostValue();

        if ($data) {

            $sliderAnemicModel = $this->sliderFactory->create();

            try {
                if ($data[SliderInterface::SLIDER_ID]) {
                    $sliderAnemicModel = $this->sliderRepository->getById($data[SliderInterface::SLIDER_ID]);
                } else {
                    unset($data[SliderInterface::SLIDER_ID]);
                }

                $this->dataObjectHelper->populateWithArray(
                    $sliderAnemicModel,
                    $data,
                    SliderInterface::class
                );

                $this->sliderRepository->save($sliderAnemicModel);

                $this->messageManager->addSuccessMessage(__('Slider has been saved.'));
            } catch (\Exception $e) {

                $this->messageManager->addErrorMessage(__("Something went wrong: %1", $e->getMessage()));
            }

            $this->_getSession()->setFormData($data);

            if ($this->getRequest()->getParam('back')) {
                return $this->_redirect('slider/slider/edit',
                    ['slider_id' => $sliderAnemicModel->getSliderId(), '_current' => true]);
            }
        }

        /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
        $resultRedirect = $this->resultRedirectFactory->create();

        return $resultRedirect->setPath('slider/*/');
    }
}
We will also add our different buttons files.

Add Block/Adminhtml/Slider/Edit/Back.php file.

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slider\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Back
 * @package Magelearn\Slider\Block\Adminhtml\Slider\Edit
 */
class Back extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Back'),
            'on_click' => $this->getBackUrl(),
            'class' => 'back',
            'sort_order' => 10
        ];
    }

    /**
     * @return string
     */
    public function getBackUrl()
    {
        return sprintf("location.href = '%s';", $this->getUrl('*/*/'));
    }
}

As per the highlighted code above add file at Block/Adminhtml/Slider/Edit/GenericButton.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slider\Edit;

use Magento\{Backend\Block\Widget\Context, Framework\App\RequestInterface, Framework\UrlInterface};

/**
 * Class GenericButton
 * @package Magelearn\Slider\Block\Adminhtml\Slider\Edit
 */
class GenericButton
{
    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * GenericButton constructor.
     * @param Context $context
     */
    public function __construct(
        Context $context
    ) {
        $this->urlBuilder = $context->getUrlBuilder();
        $this->request    = $context->getRequest();
    }

    /**
     * @return int
     */
    public function getId()
    {
        return (int)$this->request->getParam('slider_id');
    }

    /**
     * @param string $route
     * @param array $params
     * @return string
     */
    public function getUrl($route = '', $params = [])
    {
        return $this->urlBuilder->getUrl($route, $params);
    }
}

Add file at Block/Adminhtml/Slider/Edit/Reset.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slider\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Reset
 * @package Magelearn\Slider\Block\Adminhtml\Slider\Edit
 */
class Reset extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Reset'),
            'class' => 'reset',
            'on_click' => 'location.reload();',
            'sort_order' => 20
        ];
    }
}

Add file at Block/Adminhtml/Slider/Edit/Delete.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slider\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Delete
 * @package Magelearn\Slider\Block\Adminhtml\Slider\Edit
 */
class Delete extends GenericButton implements ButtonProviderInterface
{

    /**
     * @return array
     */
    public function getButtonData()
    {
        $data = [];

        if ($this->getId()) {
            $data = [
                'label' => __('Delete'),
                'class' => 'delete',
                'on_click' => 'deleteConfirm(\''
                    . __('Are you sure you want to delete this slider?')
                    . '\', \'' . $this->getDeleteUrl() . '\')',
                'sort_order' => 30,
            ];
        }
        return $data;
    }

    /**
     * @return string
     */
    public function getDeleteUrl()
    {
        return $this->getUrl('*/*/delete', ['slider_id' => $this->getId()]);
    }
}

Add file at Block/Adminhtml/Slider/Edit/Save.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slider\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Save
 * @package Magelearn\Slider\Block\Adminhtml\Slider\Edit
 */
class Save extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Save'),
            'class' => 'save primary',
            'data_attribute' => [
                'mage-init' => ['button' => ['event' => 'save']],
                'form-role' => 'save',
            ],
            'sort_order' => 40,
        ];
    }
}

Add file at Block/Adminhtml/Slider/Edit/SaveAndContinueEdit.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slider\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class SaveAndContinueEdit
 * @package Magelearn\Slider\Block\Adminhtml\Slider\Edit
 */
class SaveAndContinueEdit extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Save and Continue Edit'),
            'class' => 'save',
            'data_attribute' => [
                'mage-init' => ['button' => ['event' => 'saveAndContinueEdit']],
            ],
            'sort_order' => 60,
        ];
    }
}

For Slide management, we will add Controller/Adminhtml/Slide/Index.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slide;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\View\Result\PageFactory;

/**
 * Class Index
 * @package Magelearn\Slider\Controller\Adminhtml\Slide
 */
class Index extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var PageFactory
     */
    private $resultPageFactory;

    /**
     * Index constructor.
     * @param Context $context
     * @param PageFactory $pageFactory
     */
    public function __construct(Context $context, PageFactory $pageFactory)
    {
        parent::__construct($context);
        $this->resultPageFactory = $pageFactory;
    }

    /**
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('Magento_Backend::content_elements');
        $resultPage->getConfig()->getTitle()->prepend(__('Slides'));
        return $resultPage;
    }
}

Also add view/adminhtml/layout/slider_slide_index.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="slider_slide_listing"/>
        </referenceContainer>
    </body>
</page>

Add uiComponent file at view/adminhtml/ui_component/slider_slide_listing.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">slider_slide_listing.slide_grid_data_source</item>
            <item name="deps" xsi:type="string">slider_slide_listing.slide_grid_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">slide_columns</item>
        <item name="buttons" xsi:type="array">
            <item name="add" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slide\Edit\NewAction</item>
            <item name="back" xsi:type="array">
                <item name="name" xsi:type="string">back</item>
                <item name="label" xsi:type="string" translate="true">Back to sliders</item>
                <item name="class" xsi:type="string">back</item>
                <item name="url" xsi:type="string">slider/slider/index</item>
            </item>
        </item>
    </argument>
    <dataSource name="slide_grid_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider</argument>
            <argument name="name" xsi:type="string">slide_grid_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">slide_id</argument>
            <argument name="requestFieldName" xsi:type="string">slide_id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
                    <item name="update_url" xsi:type="url" path="mui/index/render"/>
                    <item name="storageConfig" xsi:type="array">
                        <item name="indexField" xsi:type="string">slide_id</item>
                    </item>
                    <item name="filter_url_params" xsi:type="array">
                        <item name="slider_id" xsi:type="string">*</item>
                    </item>
                </item>
            </argument>
        </argument>
    </dataSource>

    <listingToolbar name="listing_top">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="sticky" xsi:type="boolean">true</item>
            </item>
        </argument>
        <filters name="listing_filters"/>
        <paging name="listing_paging"/>
        <container name="columns_controls">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="columnsData" xsi:type="array">
                        <item name="provider" xsi:type="string">slider_slide_listing.slider_slide_listing.slide_columns</item>
                    </item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
                    <item name="displayArea" xsi:type="string">dataGridActions</item>
                </item>
            </argument>
        </container>
        <massaction name="listing_massaction">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="selectProvider" xsi:type="string">slider_slide_listing.slider_slide_listing.slide_columns.ids</item>
                    <item name="displayArea" xsi:type="string">bottom</item>
                    <item name="indexField" xsi:type="string">slide_id</item>
                </item>
            </argument>
            <action name="delete" class="Magelearn\Slider\Ui\Component\Slide\Control\MassDeleteAction">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="type" xsi:type="string">delete</item>
                        <item name="label" xsi:type="string" translate="true">Delete</item>
                        <item name="confirm" xsi:type="array">
                            <item name="title" xsi:type="string" translate="true">Delete items</item>
                            <item name="message" xsi:type="string" translate="true">Are you sure you want to delete selected items?</item>
                        </item>
                    </item>
                </argument>
            </action>
        </massaction>
    </listingToolbar>

    <columns name="slide_columns">
        <selectionsColumn name="ids">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">slide_id</item>
                </item>
            </argument>
        </selectionsColumn>

        <column name="slide_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">textRange</item>
                    <item name="sorting" xsi:type="string">asc</item>
                    <item name="label" xsi:type="string" translate="true">ID</item>
                </item>
            </argument>
        </column>

        <column name="position">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Position</item>
                </item>
            </argument>
        </column>

        <column name="image_medium" class="Magelearn\Slider\Ui\Component\Slide\Grid\Columns\Thumbnail">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/thumbnail</item>
                    <item name="sortable" xsi:type="boolean">false</item>
                    <item name="altField" xsi:type="string">title</item>
                    <item name="has_preview" xsi:type="string">1</item>
                    <item name="label" xsi:type="string" translate="true">Thumbnail</item>
                    <item name="resizeEnabled" xsi:type="boolean">false</item>
                    <item name="resizeDefaultWidth" xsi:type="string">200</item>
                </item>
            </argument>
        </column>

        <column name="title">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Title</item>
                </item>
            </argument>
        </column>

        <column name="url">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Url</item>
                </item>
            </argument>
        </column>

        <column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="sorting" xsi:type="string">desc</item>
                    <item name="label" xsi:type="string" translate="true">Created At</item>
                    <item name="dateFormat" xsi:type="string">MMM dd, YYYY, H:mm:ss</item>
                </item>
            </argument>
        </column>

        <column name="date_from" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">Start Date</item>
                </item>
            </argument>
        </column>

        <column name="date_to" class="Magento\Ui\Component\Listing\Columns\Date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">dateRange</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                    <item name="label" xsi:type="string" translate="true">End Date</item>
                </item>
            </argument>
        </column>

        <actionsColumn name="actions" class="Magelearn\Slider\Ui\Component\Slide\Grid\Columns\Actions">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="indexField" xsi:type="string">slide_id</item>
                    <item name="sortOrder" xsi:type="number">200</item>
                </item>
            </argument>
        </actionsColumn>
    </columns>
</listing>

As per highlighted code above, we will add Ui/Component/Slide/Grid/Columns/Thumbnail.php file.

<?php

namespace Magelearn\Slider\Ui\Component\Slide\Grid\Columns;

use Magento\Backend\Model\UrlInterface;
use Magelearn\Slider\Model\Slide\FileInfo;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Asset\Repository;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;

class Thumbnail extends Column
{
    public const URL_PATH_EDIT = 'slider/slide/edit';

    /**
     * @var FileInfo
     */
    private $fileInfo;
    
    /**
     * @var Repository
     */
    private $assetRepo;
    
    /**
     * @var UrlInterface
     */
    private $_backendUrl;

    /**
     * @param ContextInterface $context
     * @param UiComponentFactory $uiComponentFactory
     * @param FileInfo $fileInfo
     * @param Repository $assetRepo
     * @param UrlInterface $backendUrl
     * @param array $components
     * @param array $data
     */
    public function __construct(
        ContextInterface $context,
        UiComponentFactory $uiComponentFactory,
        FileInfo $fileInfo,
        Repository $assetRepo,
        UrlInterface $backendUrl,
        array $components = [],
        array $data = []
    ) {
        parent::__construct($context, $uiComponentFactory, $components, $data);
        $this->fileInfo = $fileInfo;
        $this->assetRepo = $assetRepo;
        $this->_backendUrl = $backendUrl;
    }

    /**
     * @param array $dataSource
     * @return array
     */
    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            $fieldName = $this->getData('name');
            foreach ($dataSource['data']['items'] as & $item) {
                $url = '';
                if ($item[$fieldName] != '') {
                    $url = $this->fileInfo->getImageUrl($item[$fieldName]);
                } else {
                    $baseImage = $this->assetRepo->getUrl('Magelearn_Slider::images/slide.png');
                }

                $item[$fieldName . '_src'] = $url;
                $item[$fieldName . '_alt'] = $this->getAlt($item) ?: '';
                $item[$fieldName . '_link'] = $this->_backendUrl->getUrl(
                    self::URL_PATH_EDIT,
                    ['slide_id' => $item['slide_id'], 'store' => $this->context->getRequestParam('store')]
                );
                $item[$fieldName . '_orig_src'] = $url;
            }
        }

        return $dataSource;
    }

    /**
     * @param $row
     * @return null
     */
    protected function getAlt($row)
    {
        $altField = $this->getData('config/altField') ?: 'title';

        return $row[$altField] ?? null;
    }
}

As per highlighted code above add file Model/Slide/FileInfo.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Model\Slide;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\File\Mime;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
use Magento\Framework\UrlInterface;

class FileInfo
{
    private const MEDIA_DIRECTORY_PATH = '/slider/images/upload/';

    /**
     * @var Filesystem
     */
    private $filesystem;

    /**
     * @var Mime
     */
    private $mime;

    /**
     * @var UrlInterface
     */
    private $url;

    /**
     * @var WriteInterface
     */
    private $mediaDirectory;

    /**
     * @param Filesystem $filesystem
     * @param Mime $mime
     * @param UrlInterface $url
     */
    public function __construct(
        Filesystem $filesystem,
        Mime $mime,
        UrlInterface $url
    ) {
        $this->filesystem = $filesystem;
        $this->mime = $mime;
        $this->url = $url;
    }

    /**
     * @param string $fileName
     * @return string
     */
    public function getImageUrl($fileName)
    {
        $filePath = $this->getFilePath($fileName);

        return $this->url->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]) . ltrim($filePath, '/');
    }

    /**
     * @param string $fileUrl
     * @return string
     */
    public function getImagePathFromUrl($fileUrl)
    {
        $mediaUrl = $this->url->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]) . ltrim(self::MEDIA_DIRECTORY_PATH, '/');

        return str_replace($mediaUrl, '', $fileUrl);
    }

    /**
     * @param string $fileName
     * @return string
     */
    public function getMimeType($fileName)
    {
        $filePath = $this->getFilePath($fileName);
        $absoluteFilePath = $this->getMediaDirectory()->getAbsolutePath($filePath);

        return $this->mime->getMimeType($absoluteFilePath);
    }

    /**
     * @param string $fileName
     * @return array
     */
    public function getStat($fileName)
    {
        $filePath = $this->getFilePath($fileName);

        return $this->getMediaDirectory()->stat($filePath);
    }

    /**
     * @param string $fileName
     * @return bool
     */
    public function isExist($fileName)
    {
        $filePath = $this->getFilePath($fileName);

        return $this->getMediaDirectory()->isExist($filePath);
    }

    /**
     * Construct and return file subpath based on filename relative to media directory
     *
     * @param string $fileName
     * @return string
     */
    private function getFilePath($fileName)
    {
        if (!$fileName) {
            return '';
        }

        return self::MEDIA_DIRECTORY_PATH . ltrim($fileName, '/');
    }

    /**
     * @return WriteInterface
     */
    private function getMediaDirectory()
    {
        if ($this->mediaDirectory === null) {
            $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
        }

        return $this->mediaDirectory;
    }
}

We will also add our actions file at Ui/Component/Slide/Grid/Columns/Actions.php

<?php

namespace Magelearn\Slider\Ui\Component\Slide\Grid\Columns;

use Magento\Ui\Component\Listing\Columns\Column;

/**
 * Class Actions
 * @package Magelearn\Slider\Ui\Component\Slide\Grid\Columns
 */
class Actions extends Column
{
    public const SLIDE_ID      = 'slide_id';
    public const SLIDER_ID     = 'slider_id';
    public const URL_PATH_EDIT = 'slider/slide/edit';

    /**
     * @param array $dataSource
     * @return array
     */
    public function prepareDataSource(array $dataSource)
    {
        if (isset($dataSource['data']['items'])) {
            foreach ($dataSource['data']['items'] as &$item) {
                $name = $this->getData('name');
                if (isset($item[self::SLIDE_ID])) {
                    $item[$name]['edit'] = [
                        'href' => $this->context->getUrl(self::URL_PATH_EDIT,
                            [
                                self::SLIDE_ID => $item[self::SLIDE_ID],
                                self::SLIDER_ID => $item[self::SLIDER_ID]
                            ]),
                        'label' => __('Edit')
                    ];
                }
            }
        }

        return $dataSource;
    }
}

We will also add MassDeleteAction file at Ui/Component/Slide/Control/MassDeleteAction.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Ui\Component\Slide\Control;

use Magento\Ui\Component\Control\Action;

class MassDeleteAction extends Action
{
    /**
     * @inheritDoc
     */
    public function prepare(): void
    {
        $config = $this->getConfiguration();
        $context = $this->getContext();
        $config['url'] = $context->getUrl(
            'slider/slide/massDelete',
            ['slider_id' => $context->getRequestParam('slider_id')]
        );

        $this->setData('config', $config);

        parent::prepare();
    }
}

Add file at Controller/Adminhtml/Slide/MassDelete.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Controller\Adminhtml\Slide;

use Exception;
use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magelearn\Slider\Model\ResourceModel\Slide\CollectionFactory;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Ui\Component\MassAction\Filter;

class MassDelete extends Action implements HttpPostActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var SlideRepositoryInterface
     */
    private $slideRepository;

    /**
     * @var CollectionFactory
     */
    private $slideCollectionFactory;

    /**
     * @var Filter
     */
    private $filter;

    /**
     * @param Context $context
     * @param SlideRepositoryInterface $slideRepository
     * @param CollectionFactory $slideCollectionFactory
     * @param Filter $filter
     */
    public function __construct(
        Context $context,
        SlideRepositoryInterface $slideRepository,
        CollectionFactory $slideCollectionFactory,
        Filter $filter
    ) {
        parent::__construct($context);
        $this->slideRepository = $slideRepository;
        $this->slideCollectionFactory = $slideCollectionFactory;
        $this->filter = $filter;
    }

    /**
     * @inheritDoc
     */
    public function execute()
    {
        try {
            $slideCollectionSize = $this->removeElements();
            $this->messageManager->addSuccessMessage(
                __('A total of %1 slide(s) have been deleted.', $slideCollectionSize)
            );
        } catch (Exception $e) {
            $this->messageManager->addErrorMessage(__('Something went wrong: %1', $e->getMessage()));
        }

        return $this->_redirect($this->_redirect->getRefererUrl());
    }

    /**
     * @return int
     * @throws LocalizedException
     */
    private function removeElements(): int
    {
        $slideCollection     = $this->filter->getCollection($this->slideCollectionFactory->create());
        $slideCollectionSize = $slideCollection->getSize();

        foreach ($slideCollection as $slideAnemicModel) {
            $this->slideRepository->delete($slideAnemicModel);
        }

        return $slideCollectionSize;
    }
}

Now as per highlighted in view/adminhtml/ui_component/slider_slide_listing.xml file, by clicking on Add slide button we will call new slide action.


For that add file at Block/Adminhtml/Slide/Edit/NewAction.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Back
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class NewAction extends GenericButton implements ButtonProviderInterface
{

    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Add new slide'),
            'on_click' => $this->getNewActionUrl(),
            'class' => 'primary',
            'sort_order' => 100
        ];
    }

    /**
     * @return string
     */
    public function getNewActionUrl()
    {
        return sprintf(
            "location.href = '%s';",
            $this->getUrl(
                '*/*/newAction',
                [
                    'slide_id' => 0,
                    'slider_id' => $this->getSliderId()
                ]
            )
        );
    }
}

As per the highlighted code above add file at Block/Adminhtml/Slide/Edit/GenericButton.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\{Backend\Block\Widget\Context, Framework\App\RequestInterface, Framework\UrlInterface};

/**
 * Class GenericButton
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class GenericButton
{
    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * GenericButton constructor.
     * @param Context $context
     */
    public function __construct(
        Context $context
    ) {
        $this->urlBuilder = $context->getUrlBuilder();
        $this->request    = $context->getRequest();
    }

    /**
     * @return int
     */
    public function getId()
    {
        return (int)$this->request->getParam('slide_id');
    }

    /**
     * @return int
     */
    public function getSliderId()
    {
        return (int)$this->request->getParam('slider_id');
    }

    /**
     * @param string $route
     * @param array $params
     * @return string
     */
    public function getUrl($route = '', $params = [])
    {
        return $this->urlBuilder->getUrl($route, $params);
    }
}

Now add Controller/Adminhtml/Slide/NewAction.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slide;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Backend\Model\View\Result\ForwardFactory;
use Magento\Framework\App\Action\HttpGetActionInterface;

/**
 * Class NewAction
 * @package Magelearn\Slider\Controller\Adminhtml\Slide
 */
class NewAction extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var ForwardFactory
     */
    private $resultForwardFactory;

    /**
     * NewAction constructor.
     * @param Context $context
     * @param ForwardFactory $resultForwardFactory
     */
    public function __construct(
        Context $context,
        ForwardFactory $resultForwardFactory
    ) {
        $this->resultForwardFactory = $resultForwardFactory;
        parent::__construct($context);
    }

    /**
     * @return mixed
     */
    public function execute()
    {
        $resultForward = $this->resultForwardFactory->create();
        return $resultForward->forward('edit');
    }
}

Now this New Action is forwarded to the Edit action.
For that Add file at Controller/Adminhtml/Slide/Edit.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Controller\Adminhtml\Slide;

use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\View\Result\PageFactory;

/**
 * Class Edit
 * @package Magelearn\Slider\Controller\Adminhtml\Slide
 */
class Edit extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var SlideRepositoryInterface
     */
    private $slideRepository;

    /**
     * @var PageFactory
     */
    private $resultPageFactory;

    /**
     * @param Context $context
     * @param SlideRepositoryInterface $slideRepository
     * @param PageFactory $pageFactory
     */
    public function __construct(
        Context $context,
        SlideRepositoryInterface $slideRepository,
        PageFactory $pageFactory
    ) {
        parent::__construct($context);
        $this->slideRepository = $slideRepository;
        $this->resultPageFactory = $pageFactory;
    }

    /**
     * @inheritDoc
     */
    public function execute()
    {
        $id = $this->getRequest()->getParam('slide_id');
        if ($id) {
            $slideModel = $this->slideRepository->getById($id);
            $pageTitle = __('Edit slide "%1"', $slideModel->getTitle());
        } else {
            $pageTitle = __('Add new slide');
        }

        $resultPage = $this->resultPageFactory->create();
        $resultPage->getConfig()->getTitle()->prepend($pageTitle);

        return $resultPage;
    }
}

Also add layout file at view/adminhtml/layout/slider_slide_edit.xml

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="styles"/>
    <update handle="editor"/>
    <head>
        <link src="Magelearn_Slider::js/validation/url.js"/>
    </head>
    <body>
        <referenceContainer name="content">
            <uiComponent name="slider_slide_form"/>
        </referenceContainer>
    </body>
</page>

As per highlighted code above we will also add our validation JS for URL validation at view/adminhtml/web/js/validation/url.js

require([
    'jquery',
    'Magento_Ui/js/lib/validation/validator',
    'jquery-ui-modules/widget',
    'mage/translate'
], function ($, validator) {
    'use strict';

    validator.addRule(
        'magelearn-validate-slider-url',
        function (v) {
            if ($.mage.isEmptyNoTrim(v)) {
                return true;
            }

            v = (v || '').replace(/^\s+/, '').replace(/\s+$/, '');

            return (/^(http|https|ftp):\/\/(([A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))(\.[A-Z0-9]([A-Z0-9_-]*[A-Z0-9]|))*)(:(\d+))?(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(v) || //eslint-disable-line max-len
                (/^\/(\/[A-Z0-9~](([A-Z0-9_~-]|\.)*[A-Z0-9~]|))*\/?(.*)?$/i).test(v);
        },
        $.mage.__('Please enter a valid absolute or relative URL. For example http://www.example.com or /page.')
    );
});

As per highlighted code above add uiComponent file at view/adminhtml/ui_component/slider_slide_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">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">slider_slide_form.slide_form_data_source</item>
            <item name="deps" xsi:type="string">slider_slide_form.slide_form_data_source</item>
        </item>
        <item name="label" xsi:type="string" translate="true">Slide Form</item>
        <item name="config" xsi:type="array">
            <item name="dataScope" xsi:type="string">data</item>
            <item name="namespace" xsi:type="string">slide_form</item>
        </item>
        <item name="template" xsi:type="string">templates/form/collapsible</item>
        <item name="buttons" xsi:type="array">
            <item name="back" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slide\Edit\Back</item>
            <item name="reset" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slide\Edit\Reset</item>
            <item name="delete" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slide\Edit\Delete</item>
            <item name="save" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slide\Edit\Save</item>
            <item name="save_and_continue" xsi:type="string">Magelearn\Slider\Block\Adminhtml\Slide\Edit\SaveAndContinueEdit</item>
        </item>
    </argument>
    <dataSource name="slide_form_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Magelearn\Slider\Ui\Component\Slide\DataProvider</argument>
            <argument name="name" xsi:type="string">slide_form_data_source</argument>
            <argument name="primaryFieldName" xsi:type="string">slide_id</argument>
            <argument name="requestFieldName" xsi:type="string">slide_id</argument>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="submit_url" xsi:type="url" path="*/*/save"/>
                </item>
            </argument>
        </argument>
        <argument name="data" xsi:type="array">
            <item name="js_config" xsi:type="array">
                <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
            </item>
        </argument>
    </dataSource>

    <fieldset name="general">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">General Information</item>
                <item name="sortOrder" xsi:type="number">10</item>
                <item name="collapsible" xsi:type="boolean">false</item>
            </item>
        </argument>

        <field name="slide_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">false</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">slide_id</item>
                </item>
            </argument>
        </field>

        <field name="slider_id">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">false</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">slider_id</item>
                </item>
            </argument>
        </field>

        <field name="title">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Title</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="sortOrder" xsi:type="number">0</item>
                    <item name="source" xsi:type="string">title</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>
        
        <field name="content">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Content</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">textarea</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                    <item name="source" xsi:type="string">content</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                </item>
            </argument>
        </field>

        <field name="url" formElement="input" sortOrder="20">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="source" xsi:type="string">url</item>
                </item>
            </argument>
            <settings>
                <dataType>text</dataType>
                <label translate="true">Url</label>
                <validation>
                    <rule name="magelearn-validate-slider-url" xsi:type="boolean">true</rule>
                </validation>
                <visible>true</visible>
            </settings>
        </field>

        <field name="position">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Position</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="sortOrder" xsi:type="number">30</item>
                    <item name="source" xsi:type="string">position</item>
                    <item name="validation" xsi:type="array">
                        <item name="validate-zero-or-greater" xsi:type="boolean">true</item>
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                </item>
            </argument>
        </field>

        <field name="status">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magelearn\Slider\Ui\Component\Form\Field\Status</item>
                <item name="config" xsi:type="array">
                    <item name="sortOrder" xsi:type="number">40</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Status</item>
                    <item name="formElement" xsi:type="string">select</item>
                    <item name="source" xsi:type="string">status</item>
                    <item name="dataScope" xsi:type="string">status</item>
                    <item name="default" xsi:type="string">1</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>

        <field name="date_from">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">Start Date</item>
                    <item name="formElement" xsi:type="string">date</item>
                    <item name="source" xsi:type="string">date_from</item>
                    <item name="sortOrder" xsi:type="number">50</item>
                    <item name="dataScope" xsi:type="string">date_from</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                    <item name="options" xsi:type="array">
                        <item name="dateFormat" xsi:type="string">yyyy-MM-dd</item>
                        <item name="timeFormat" xsi:type="string">HH:mm:ss</item>
                        <item name="showsTime" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>

        <field name="date_to">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">End Date</item>
                    <item name="formElement" xsi:type="string">date</item>
                    <item name="source" xsi:type="string">date_to</item>
                    <item name="sortOrder" xsi:type="number">60</item>
                    <item name="dataScope" xsi:type="string">date_to</item>
                    <item name="options" xsi:type="array">
                        <item name="dateFormat" xsi:type="string">yyyy-MM-dd</item>
                        <item name="timeFormat" xsi:type="string">HH:mm:ss</item>
                        <item name="showsTime" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>
    
        <field name="is_active_countdown" sortOrder="70" formElement="select">
            <settings>
                <dataType>text</dataType>
                <dataScope>is_active_countdown</dataScope>
                <label translate="true">Counter enabled</label>
                <switcherConfig>
                    <rules>
                        <rule name="0">
                            <value>0</value>
                            <actions>
                                <action name="0">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_date_from</target>
                                    <callback>hide</callback>
                                </action>
                                <action name="1">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_date_to</target>
                                    <callback>hide</callback>
                                </action>
                                <action name="2">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_color</target>
                                    <callback>hide</callback>
                                </action>
                                <action name="3">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_background_color</target>
                                    <callback>hide</callback>
                                </action>
                            </actions>
                        </rule>
                        <rule name="1">
                            <value>1</value>
                            <actions>
                                <action name="0">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_date_from</target>
                                    <callback>show</callback>
                                </action>
                                <action name="1">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_date_to</target>
                                    <callback>show</callback>
                                </action>
                                <action name="2">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_color</target>
                                    <callback>show</callback>
                                </action>
                                <action name="3">
                                    <target>slider_slide_form.slider_slide_form.general.countdown_background_color</target>
                                    <callback>show</callback>
                                </action>
                            </actions>
                        </rule>
                    </rules>
                    <enabled>true</enabled>
                </switcherConfig>
            </settings>
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magelearn\Slider\Ui\Component\Form\Field\Countdown</item>
                <item name="config" xsi:type="array">
                    <item name="source" xsi:type="string">is_active_countdown</item>
                    <item name="default" xsi:type="string">0</item>
                    <item name="notice" xsi:type="string">If counter is enabled then "Countdown date to" must be set, otherwise it won't be displayed</item>
                </item>
            </argument>
        </field>
        
        <field name="countdown_date_from">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">Countdown date from</item>
                    <item name="formElement" xsi:type="string">date</item>
                    <item name="source" xsi:type="string">countdown_date_from</item>
                    <item name="sortOrder" xsi:type="number">80</item>
                    <item name="dataScope" xsi:type="string">countdown_date_from</item>
                    <item name="notice" xsi:type="string">When countdown starts displaying</item>
                    <item name="options" xsi:type="array">
                        <item name="dateFormat" xsi:type="string">yyyy-MM-dd</item>
                        <item name="timeFormat" xsi:type="string">HH:mm:ss</item>
                        <item name="showsTime" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>
        
        <field name="countdown_date_to">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">Countdown date to</item>
                    <item name="formElement" xsi:type="string">date</item>
                    <item name="source" xsi:type="string">countdown_date_to</item>
                    <item name="sortOrder" xsi:type="number">90</item>
                    <item name="dataScope" xsi:type="string">countdown_date_to</item>
                    <item name="notice" xsi:type="string">When countdown ends displaying</item>
                    <item name="options" xsi:type="array">
                        <item name="dateFormat" xsi:type="string">yyyy-MM-dd</item>
                        <item name="timeFormat" xsi:type="string">HH:mm:ss</item>
                        <item name="showsTime" xsi:type="boolean">true</item>
                    </item>
                </item>
            </argument>
        </field>
        
        <field name="countdown_color" sortOrder="100"
            formElement="colorPicker">
            <settings>
                <label translate="true">Countdown color</label>
                <componentType>colorPicker</componentType>
                <dataScope>countdown_color</dataScope>
                <dataType>text</dataType>
                <notice>Use CSS value like "#ffffff" or "red"</notice>
            </settings>
            <formElements>
                <colorPicker>
                    <settings>
                        <colorPickerMode>full</colorPickerMode>
                        <colorFormat>hex</colorFormat>
                    </settings>
                </colorPicker>
            </formElements>
        </field>
        
        <field name="countdown_background_color" sortOrder="110"
            formElement="colorPicker">
            <settings>
                <label translate="true">Countdown background color</label>
                <componentType>colorPicker</componentType>
                <dataScope>countdown_background_color</dataScope>
                <dataType>text</dataType>
                <notice>Use CSS value like "#ffffff" or "red"</notice>
            </settings>
            <formElements>
                <colorPicker>
                    <settings>
                        <colorPickerMode>full</colorPickerMode>
                        <colorFormat>hex</colorFormat>
                    </settings>
                </colorPicker>
            </formElements>
        </field>
    </fieldset>
    
    <fieldset name="daily_deal_information">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">Daily Deal Information</item>
                <item name="sortOrder" xsi:type="number">170</item>
                <item name="collapsible" xsi:type="boolean">true</item>
            </item>
        </argument>
        
        <field name="show_daily_deal" sortOrder="70" formElement="select">
            <settings>
                <dataType>text</dataType>
                <dataScope>is_active_countdown</dataScope>
                <label translate="true">Counter enabled</label>
                <switcherConfig>
                    <rules>
                        <rule name="0">
                            <value>0</value>
                            <actions>
                                <action name="0">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_product_id</target>
                                    <callback>disable</callback>
                                </action>
                                <action name="1">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_color</target>
                                    <callback>disable</callback>
                                </action>
                                <action name="2">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_background_color</target>
                                    <callback>disable</callback>
                                </action>
                                <action name="3">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_top</target>
                                    <callback>disable</callback>
                                </action>
                                <action name="4">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_left</target>
                                    <callback>disable</callback>
                                </action>
                            </actions>
                        </rule>
                        <rule name="1">
                            <value>1</value>
                            <actions>
                                <action name="0">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_product_id</target>
                                    <callback>enable</callback>
                                </action>
                                <action name="1">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_color</target>
                                    <callback>enable</callback>
                                </action>
                                <action name="2">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_background_color</target>
                                    <callback>enable</callback>
                                </action>
                                <action name="3">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_top</target>
                                    <callback>enable</callback>
                                </action>
                                <action name="3">
                                    <target>slider_slide_form.slider_slide_form.daily_deal_information.daily_deal_left</target>
                                    <callback>enable</callback>
                                </action>
                            </actions>
                        </rule>
                    </rules>
                    <enabled>true</enabled>
                </switcherConfig>
            </settings>
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magelearn\Slider\Ui\Component\Form\Field\Status</item>
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Show daily deal</item>
                    <item name="source" xsi:type="string">show_daily_deal</item>
                    <item name="dataScope" xsi:type="string">show_daily_deal</item>
                    <item name="default" xsi:type="string">0</item>
                </item>
            </argument>
        </field>
        <field name="daily_deal_product_id" component="Magelearn_Slider/js/components/select-product" formElement="select" sortOrder="80">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="filterOptions" xsi:type="boolean">true</item><!--to add filter in select-ui-->
                    <item name="multiple" xsi:type="boolean">true</item><!--select multiple or not-->
                    <item name="showCheckbox" xsi:type="boolean">true</item>
                    <!-- checkbox will not display if multiple == false -->
                    <item name="disableLabel" xsi:type="boolean">true</item>
                </item>
            </argument>
            <settings>
                <required>false</required>
                <validation>
                    <rule name="required-entry" xsi:type="boolean">false</rule>
                </validation>
                <elementTmpl>ui/grid/filters/elements/ui-select</elementTmpl>
                <label translate="true">Select Product</label>
                <dataScope>daily_deal_product_id</dataScope>
                <componentType>field</componentType>
                <listens>
                    <link name="${ $.namespace }.${ $.namespace }:responseData">setParsed</link>
                </listens>
            </settings>
            <formElements>
                <select>
                    <settings>
                        <options class="Magelearn\Slider\Ui\Component\Create\Form\Product\Options"/>
                    </settings>
                </select>
            </formElements>
        </field>
        <field name="daily_deal_color" sortOrder="100"
            formElement="colorPicker">
            <settings>
                <label translate="true">Daily Deal Color</label>
                <componentType>colorPicker</componentType>
                <dataScope>daily_deal_color</dataScope>
                <dataType>text</dataType>
                <notice>Use CSS value like "#ffffff" or "red"</notice>
            </settings>
            <formElements>
                <colorPicker>
                    <settings>
                        <colorPickerMode>full</colorPickerMode>
                        <colorFormat>hex</colorFormat>
                    </settings>
                </colorPicker>
            </formElements>
        </field>
        <field name="daily_deal_background_color" sortOrder="110"
            formElement="colorPicker">
            <settings>
                <label translate="true">Daily Deal Background Color</label>
                <componentType>colorPicker</componentType>
                <dataScope>daily_deal_background_color</dataScope>
                <dataType>text</dataType>
                <notice>Use CSS value like "#f0f0f0" or "white"</notice>
            </settings>
            <formElements>
                <colorPicker>
                    <settings>
                        <colorPickerMode>full</colorPickerMode>
                        <colorFormat>hex</colorFormat>
                    </settings>
                </colorPicker>
            </formElements>
        </field>
        
        <field name="daily_deal_top">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">Daily Deal Top</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">daily_deal_top</item>
                    <item name="sortOrder" xsi:type="number">120</item>
                    <item name="dataScope" xsi:type="string">daily_deal_top</item>
                    <item name="notice" xsi:type="string">Use CSS unit value like "20px" or "10%", hyphen is allowed, then top becomes dottom</item>
                </item>
            </argument>
        </field>
        <field name="daily_deal_left">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="label" xsi:type="string" translate="true">Daily Deal left</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="source" xsi:type="string">daily_deal_left</item>
                    <item name="sortOrder" xsi:type="number">120</item>
                    <item name="dataScope" xsi:type="string">daily_deal_left</item>
                    <item name="notice" xsi:type="string">Use CSS unit value like "10px" or "15%", hyphen is allowed, then left becomes right</item>
                </item>
            </argument>
        </field>
    </fieldset>
    
    <fieldset name="video_information">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">Video</item>
                <item name="sortOrder" xsi:type="number">180</item>
                <item name="collapsible" xsi:type="boolean">true</item>
            </item>
        </argument>
        <field name="video">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">Magelearn\Slider\Ui\Component\Form\Field\Video</item>
                <item name="config" xsi:type="array">
                    <item name="sortOrder" xsi:type="number">10</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Video source</item>
                    <item name="formElement" xsi:type="string">select</item>
                    <item name="source" xsi:type="string">video</item>
                    <item name="dataScope" xsi:type="string">video</item>
                    <item name="default" xsi:type="string">0</item>
                </item>
            </argument>
            <settings>
                <additionalClasses>
                    <class name="admin__field-min-height">true</class>
                </additionalClasses>
                <tooltip>
                    <description translate="true">
                        <![CDATA[
                        <span style="color: blue;">
                            <p>If video content is there then only video will be display on full screen slide.</p>
                        </span>
                        ]]>
                    </description>
                </tooltip>
            </settings>
        </field>
        <field name="video_content">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string">Video content</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">textarea</item>
                    <item name="sortOrder" xsi:type="number">20</item>
                    <item name="source" xsi:type="string">video_content</item>
                    <item name="label" xsi:type="string" translate="true">Add URL of Video</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                </item>
            </argument>
            <settings>
                <additionalClasses>
                    <class name="admin__field-min-height">true</class>
                </additionalClasses>
                <tooltip>
                    <description translate="true">
                        <![CDATA[
                        <ol>
                            <li>For You Tube: Add embed url like "https://www.youtube.com/embed/xxxxxxx", replace xxxxxxx with your you tube id.</li>
                            <li>For Vimeo: Add embed url like "https://player.vimeo.com/video/xxxxxxx", replace xxxxxxx with your Vimeo id.</li>
                            <li>For HTML: Add your hosted video url "https://yourdomain.com/path_to_video.mp4".</li>
                        </ol>
                        ]]>
                    </description>
                </tooltip>
            </settings>
        </field>
    </fieldset>
    
    <fieldset name="images_information">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">Images</item>
                <item name="sortOrder" xsi:type="number">190</item>
                <item name="collapsible" xsi:type="boolean">true</item>
            </item>
        </argument>

        <field name="image_thumbnail" sortOrder="10" formElement="fileUploader">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="baseTmpPath" xsi:type="string">slider/tmp/images</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="previewTmpl" xsi:type="string">Magelearn_Slider/image-preview</item>
                    <item name="uploaderConfig" xsi:type="array">
                        <item name="url" xsi:type="url" path="slider/slide_image/upload"/>
                    </item>
                </item>
            </argument>
            <settings>
                <dataType>string</dataType>
                <elementTmpl>ui/form/element/uploader/uploader</elementTmpl>
                <label translate="true">Thumbnail</label>
            </settings>
        </field>

        <field name="image_medium" sortOrder="20" formElement="fileUploader">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="baseTmpPath" xsi:type="string">slider/tmp/images</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="previewTmpl" xsi:type="string">Magelearn_Slider/image-preview</item>
                    <item name="uploaderConfig" xsi:type="array">
                        <item name="url" xsi:type="url" path="slider/slide_image/upload"/>
                    </item>
                </item>
            </argument>
            <settings>
                <dataType>string</dataType>
                <elementTmpl>ui/form/element/uploader/uploader</elementTmpl>
                <label translate="true">Image for desktop</label>
                <validation>
                    <rule name="required-entry" xsi:type="boolean">true</rule>
                </validation>
            </settings>
        </field>

        <field name="image_small" sortOrder="30" formElement="fileUploader">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="baseTmpPath" xsi:type="string">slider/tmp/images</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="previewTmpl" xsi:type="string">Magelearn_Slider/image-preview</item>
                    <item name="uploaderConfig" xsi:type="array">
                        <item name="url" xsi:type="url" path="slider/slide_image/upload"/>
                    </item>
                </item>
            </argument>
            <settings>
                <dataType>string</dataType>
                <elementTmpl>ui/form/element/uploader/uploader</elementTmpl>
                <label translate="true">Image for tablet</label>
            </settings>
        </field>

        <field name="image_mobile" sortOrder="40" formElement="fileUploader">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="baseTmpPath" xsi:type="string">slider/tmp/images</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="previewTmpl" xsi:type="string">Magelearn_Slider/image-preview</item>
                    <item name="uploaderConfig" xsi:type="array">
                        <item name="url" xsi:type="url" path="slider/slide_image/upload"/>
                    </item>
                </item>
            </argument>
            <settings>
                <dataType>string</dataType>
                <elementTmpl>ui/form/element/uploader/uploader</elementTmpl>
                <label translate="true">Image for mobile</label>
            </settings>
        </field>
    </fieldset>
</form>

Now as per highlighted code above we will add our DataProvider class file at Ui/Component/Slide/DataProvider.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Ui\Component\Slide;

use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Model\ResourceModel\Slide\Collection;
use Magelearn\Slider\Model\ResourceModel\Slide\CollectionFactory;
use Magelearn\Slider\Model\Slide\FileInfo;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Ui\DataProvider\Modifier\PoolInterface;
use Magento\Ui\DataProvider\ModifierPoolDataProvider;

class DataProvider extends ModifierPoolDataProvider
{
    public const DATA_PERSISTOR_KEY = 'magelearn_slider_slide';

    public const IMAGE_PARAMS = [
        SlideInterface::IMAGE_THUMBNAIL,
        SlideInterface::IMAGE_MEDIUM,
        SlideInterface::IMAGE_SMALL,
        SlideInterface::IMAGE_MOBILE
    ];

    /**
     * @var Collection
     */
    protected $collection;

    /**
     * @var DataPersistorInterface
     */
    private $dataPersistor;

    /**
     * @var FileInfo
     */
    private $fileInfo;

    /**
     * @var array
     */
    private $loadedData;

    /**
     * @var RequestInterface
     */
    private $request;

    /**
     * @param string $name
     * @param string $primaryFieldName
     * @param string $requestFieldName
     * @param CollectionFactory $collectionFactory
     * @param DataPersistorInterface $dataPersistor
     * @param FileInfo $fileInfo
     * @param RequestInterface $request
     * @param array $meta
     * @param array $data
     * @param PoolInterface|null $pool
     */
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        CollectionFactory $collectionFactory,
        DataPersistorInterface $dataPersistor,
        FileInfo $fileInfo,
        RequestInterface $request,
        array $meta = [],
        array $data = [],
        PoolInterface $pool = null
    ) {
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool);
        $this->collection = $collectionFactory->create();
        $this->dataPersistor = $dataPersistor;
        $this->fileInfo = $fileInfo;
        $this->request = $request;
    }

    /**
     * @inheritDoc
     */
    public function getData(): array
    {
        if (isset($this->loadedData)) {
            return $this->loadedData;
        }

        $this->loadedData = [];
        $this->loadedData[0][SlideInterface::SLIDER_ID] = (int)$this->request->getParam('slider_id');

        $items = $this->collection->getItems();

        foreach ($items as $slide) {
            $data = $slide->getData();
            foreach (self::IMAGE_PARAMS as $imageParam) {
                $data = $this->addImage($data, $imageParam);
            }
            $this->loadedData[$slide->getId()] = $data;
            if(isset($data['daily_deal_product_id']) && !empty($data['daily_deal_product_id'])) {
                $this->loadedData[$slide->getId()]['daily_deal_product_id'] = explode(",", $slide->getData('daily_deal_product_id'));
            }
        }

        $data = $this->dataPersistor->get(self::DATA_PERSISTOR_KEY);
        if (!empty($data)) {
            $slide = $this->collection->getNewEmptyItem();
            $slide->setData($data);
            $this->loadedData[$slide->getId()] = $slide->getData();
            $this->dataPersistor->clear(self::DATA_PERSISTOR_KEY);
        }

        return $this->loadedData;
    }

    /**
     * @param array $data
     * @param string $paramName
     * @return array
     */
    private function addImage(array $data, string $paramName): array
    {
        $fileName = $data[$paramName] ?? '';

        if (!$fileName) {
            return $data;
        }

        unset($data[$paramName]);

        if ($this->fileInfo->isExist($fileName)) {
            $stat = $this->fileInfo->getStat($fileName);
            $mime = $this->fileInfo->getMimeType($fileName);

            $data[$paramName][0]['name'] = basename($fileName);
            $data[$paramName][0]['url'] = $this->fileInfo->getImageUrl($fileName);
            $data[$paramName][0]['size'] = isset($stat) ? $stat['size'] : 0;
            $data[$paramName][0]['type'] = $mime;
        }

        return $data;
    }
}

Add file at Ui/Component/Form/Field/Status.php

<?php

namespace Magelearn\Slider\Ui\Component\Form\Field;

use Magento\Framework\Option\ArrayInterface;

/**
 * Class Status
 * @package Magelearn\Slider\Ui\Component\Form\Field
 */
class Status implements ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        $arr     = $this->toArray();
        $options = [];
        foreach ($arr as $key => $value) {
            $options[] = [
                'value' => $key,
                'label' => $value
            ];
        }

        return $options;
    }

    /**
     * @return array
     */
    public function toArray()
    {
        $status = [
            '1' => 'Enabled',
            '0' => 'Disabled',

        ];

        return $status;
    }
}

Add file at Ui/Component/Form/Field/Countdown.php

<?php

declare(strict_types = 1);

namespace Magelearn\Slider\Ui\Component\Form\Field;

use Magento\Framework\Option\ArrayInterface;

class Countdown implements ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        $arr     = $this->toArray();
        $options = [];
        foreach ($arr as $key => $value) {
            $options[] = [
                'value' => $key,
                'label' => $value
            ];
        }
        return $options;
    }

    /**
     * @return array
     */
    public function toArray()
    {
        $type = [
            '1' => 'Yes',
            '0' => 'No',
        ];
        return $type;
    }
}

Add file at Ui/Component/Form/Field/Video.php

<?php

declare(strict_types = 1);

namespace Magelearn\Slider\Ui\Component\Form\Field;

use Magento\Framework\Option\ArrayInterface;

class Video implements ArrayInterface
{
    /**
     * @return array
     */
    public function toOptionArray()
    {
        $arr     = $this->toArray();
        $options = [];

        foreach ($arr as $key => $value) {
            $options[] = [
                'value' => $key,
                'label' => $value
            ];
        }
        return $options;
    }

    /**
     * @return array
     */
    public function toArray()
    {
        $type = [
            '3' => 'HTML',
            '2' => 'Vimeo',
            '1' => 'YouTube',
            '0' => 'Disabled',

        ];
        return $type;
    }
}

Now to give product selection options for Daily deal add JS components file at view/adminhtml/web/js/components/select-product.js

define([
    'Magento_Ui/js/form/element/ui-select'
], function (Select) {
    'use strict';
    return Select.extend({
        /**
         * Parse data and set it to options.
         *
         * @param {Object} data - Response data object.
         * @returns {Object}
         */
        setParsed: function (data) {
            var option = this.parseData(data);
            if (data.error) {
                return this;
            }
            this.options([]);
            this.setOption(option);
            this.set('newOption', option);
        },
        /**
         * Normalize option object.
         *
         * @param {Object} data - Option object.
         * @returns {Object}
         */
        parseData: function (data) {
            console.log(data);
            return {
                value: data.product.entity_id,
                label: data.product.sku
            };
        }
    });
});

Also add Ui/Component/Create/Form/Product/Options.php file.

<?php
namespace Magelearn\Slider\Ui\Component\Create\Form\Product;

use Magento\Framework\Data\OptionSourceInterface;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Framework\App\RequestInterface;

/**
 * Class Options
 *
 * @package Magelearn\CodeSample\Ui\Component\Create\Form\Product
 */
class Options implements OptionSourceInterface
{

    /**
     * @var ProductCollectionFactory
     */
    protected $productCollectionFactory;

    /**
     * @var RequestInterface
     */
    protected $request;

    /**
     * @var array
     */
    protected $productsArray;

    /**
     * @param ProductCollectionFactory $productCollectionFactory
     * @param RequestInterface $request
     */
    public function __construct(

        ProductCollectionFactory $productCollectionFactory,
        RequestInterface $request
    ) {
        $this->productCollectionFactory = $productCollectionFactory;
        $this->request = $request;
    }

    /**
     * @return array|null
     */
    public function toOptionArray()
    {
        return $this->getProductArray();
    }

    /**
     * @return array|null
     */
    protected function getProductArray() {
        if ($this->productsArray === null) {
            $productCollection = $this->productCollectionFactory->create();
            $productCollection->addAttributeToSelect('*');
            $productCollection->setPageSize(10);
            /* setPageSize if you are facing problem when loading admin grid page */

            foreach($productCollection as $product) {
                $productId = $product->getEntityId();
                if (!isset($productById[$productId])) {
                    $productById[$productId] = [
                        'value' => $productId
                    ];
                }
                $productById[$productId]['label'] = $product->getSku();
                
            }

            $this->productsArray = $productById;
        }
            return $this->productsArray;
    }
}

Also add Image Preview template file at view/adminhtml/web/template/image-preview.html

<div class="file-uploader-summary">
    <div class="file-uploader-preview">
        <a attr="href: $parent.getFilePreview($file)" target="_blank">
            <img
                class="preview-image"
                tabindex="0"
                event="load: $parent.onPreviewLoad.bind($parent)"
                attr="
                    src: $parent.getFilePreview($file),
                    alt: $file.name">
        </a>

        <div class="actions">
            <button
                type="button"
                class="action-remove"
                data-role="delete-button"
                attr="title: $t('Delete image')"
                click="$parent.removeFile.bind($parent, $file)">
                <span translate="'Delete image'"/>
            </button>
        </div>
    </div>

    <div class="file-uploader-filename" text="$file.name"/>
    <div class="file-uploader-meta">
        <text args="$file.previewWidth"/>x<text args="$file.previewHeight"/>
    </div>
</div>

Now we will add different buttons on the slide.
For that add Block/Adminhtml/Slide/Edit/Back.php

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Back
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class Back extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Back'),
            'on_click' => $this->getBackUrl(),
            'class' => 'back',
            'sort_order' => 10
        ];
    }

    /**
     * @return string
     */
    public function getBackUrl()
    {
        return sprintf("location.href = '%s';", $this->getUrl('*/*/', ['slider_id' => $this->getSliderId()]));
    }
}

Add Block/Adminhtml/Slide/Edit/Delete.php file.

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Delete
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class Delete extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        $data = [];

        if ($this->getId()) {
            $data = [
                'label' => __('Delete'),
                'class' => 'delete',
                'on_click' => 'deleteConfirm(\''
                    . __('Are you sure you want to delete this slide?')
                    . '\', \'' . $this->getDeleteUrl() . '\')',
                'sort_order' => 30,
            ];
        }
        return $data;
    }

    /**
     * @return string
     */
    public function getDeleteUrl()
    {
        return $this->getUrl('*/*/delete', ['slide_id' => $this->getId(), 'slider_id' => $this->getSliderId()]);
    }
}

Add Block/Adminhtml/Slide/Edit/Reset.php file.

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Reset
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class Reset extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Reset'),
            'class' => 'reset',
            'on_click' => 'location.reload();',
            'sort_order' => 20
        ];
    }
}

Add Block/Adminhtml/Slide/Edit/Save.php file.

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class Save
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class Save extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Save Slide'),
            'class' => 'save primary',
            'data_attribute' => [
                'mage-init' => ['button' => ['event' => 'save']],
                'form-role' => 'save',
            ],
            'sort_order' => 40,
        ];
    }
}

Add Block/Adminhtml/Slide/Edit/SaveAndContinueEdit.php file.

<?php

namespace Magelearn\Slider\Block\Adminhtml\Slide\Edit;

use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;

/**
 * Class SaveAndContinueEdit
 * @package Magelearn\Slider\Block\Adminhtml\Slide\Edit
 */
class SaveAndContinueEdit extends GenericButton implements ButtonProviderInterface
{
    /**
     * @return array
     */
    public function getButtonData()
    {
        return [
            'label' => __('Save and Continue Edit'),
            'class' => 'save',
            'data_attribute' => [
                'mage-init' => ['button' => ['event' => 'saveAndContinueEdit']],
            ],
            'sort_order' => 60,
        ];
    }
}

Now to save the slide data add Controller/Adminhtml/Slide/Save.php file.

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slide;

use Exception;
use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Api\Data\SlideInterfaceFactory;
use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magelearn\Slider\Model\Slide;
use Magelearn\Slider\Ui\Component\Slide\DataProvider;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\Exception\LocalizedException;

class Save extends Action implements HttpPostActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var SlideInterfaceFactory
     */
    private $slideFactory;

    /**
     * @var SlideRepositoryInterface
     */
    private $slideRepository;

    /**
     * @var DataPersistorInterface
     */
    private $dataPersistor;
    
    protected $logger;

    /**
     * @param Context $context
     * @param SlideInterfaceFactory $slideFactory
     * @param SlideRepositoryInterface $slideRepository
     * @param DataPersistorInterface $dataPersistor
     */
    public function __construct(
        Context $context,
        SlideInterfaceFactory $slideFactory,
        SlideRepositoryInterface $slideRepository,
        DataPersistorInterface $dataPersistor,
        \Psr\Log\LoggerInterface $logger
    ) {
        parent::__construct($context);
        $this->slideFactory     = $slideFactory;
        $this->slideRepository  = $slideRepository;
        $this->dataPersistor = $dataPersistor;
        $this->logger = $logger;
    }

    /**
     * Save slide
     */
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $data = $this->getRequest()->getPostValue();

        if ($data) {
            if (empty($data[SlideInterface::SLIDE_ID])) {
                $data[SlideInterface::SLIDE_ID] = null;
            }

            /** @var SlideInterface|Slide $model */
            $model = $this->slideFactory->create();
            $id = $this->_request->getParam(SlideInterface::SLIDE_ID);

            if ($id) {
                try {
                    $model = $this->slideRepository->getById($id);
                } catch (LocalizedException $e) {
                    $this->messageManager->addErrorMessage(__('This slide no longer exists.'));

                    return $resultRedirect->setPath('*/*/');
                }
            }
            if(isset($data['daily_deal_product_id']) && is_array($data['daily_deal_product_id']) && !empty($data['daily_deal_product_id'])) {
                $data['daily_deal_product_id'] = trim(implode(",", $data['daily_deal_product_id']), ",");
            }
            $model->setData($data);

            try {
                $slide = $this->slideRepository->save($model);
                $this->messageManager->addSuccessMessage(__('Slide has been saved.'));
                $this->dataPersistor->clear(DataProvider::DATA_PERSISTOR_KEY);

                if ($this->getRequest()->getParam('back')) {
                    return $resultRedirect->setPath('*/*/edit', [
                        SlideInterface::SLIDE_ID => $slide->getSlideId(),
                        SlideInterface::SLIDER_ID => $slide->getSliderId()
                    ]);
                }

                return $resultRedirect->setPath('*/*/', [
                    SlideInterface::SLIDER_ID => $slide->getSliderId()
                ]);
            } catch (LocalizedException $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
            } catch (Exception $e) {
                $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the Slide.'));
            }

            $this->dataPersistor->set(DataProvider::DATA_PERSISTOR_KEY, $data);

            return $resultRedirect->setPath('*/*/edit', [
                SlideInterface::SLIDE_ID => $id,
                SlideInterface::SLIDER_ID => $model->getSliderId()
            ]);
        }

        return $this->_redirect('*/*/', ['slider_id' => $data['slider_id']]);
    }
}

Add file at Controller/Adminhtml/Slide/Delete.php

<?php

namespace Magelearn\Slider\Controller\Adminhtml\Slide;

use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magento\Backend\App\{Action, Action\Context};
use Magento\Framework\App\Action\HttpGetActionInterface;

/**
 * Class Delete
 * @package Magelearn\Slider\Controller\Adminhtml\Slide
 */
class Delete extends Action implements HttpGetActionInterface
{
    public const ADMIN_RESOURCE = 'Magelearn_Slider::slide';

    /**
     * @var SlideRepositoryInterface
     */
    private $slideRepository;

    /**
     * Delete constructor.
     * @param Context $context
     * @param SlideRepositoryInterface $slideRepository
     */
    public function __construct(
        Context $context,
        SlideRepositoryInterface $slideRepository
    ) {
        parent::__construct($context);
        $this->slideRepository = $slideRepository;
    }

    /**
     * @param $id
     */
    protected function removeSlide($id)
    {
        try {
            $this->slideRepository->deleteById($id);
        } catch (\Exception $e) {
            $this->messageManager->addErrorMessage($e->getMessage());
        }
    }

    /**
     * @return \Magento\Framework\App\ResponseInterface
     */
    public function execute()
    {
        $id = $this->getRequest()->getParam('slide_id');
        $this->removeSlide($id);
        $this->messageManager->addSuccessMessage(__('Slide has been deleted.'));

        return $this->_redirect('*/*/', ['slider_id' => $this->getRequest()->getParam('slider_id')]);
    }
}
Now to add Homepage Slider on Front end add below line of codes in home-page-block 

{{block class="Magelearn\Slider\Block\Slider" name="slider_list" template="Magelearn_Slider::slider.phtml" slider_id="1"}}

Now as per above code add Block and Template file.

Add file at Block/Slider.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Block;

use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Api\SliderManagementInterface;
use Magelearn\Slider\Helper\Config as ConfigHelper;
use Magelearn\Slider\Model\Slide\FileInfo;
use Magento\Catalog\Model\ProductRepository;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Pricing\Price\FinalPrice;
use Magento\Catalog\Helper\Image;
use Magento\Framework\DataObject;
use Magento\Framework\Escaper;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ActionInterface;
use Magento\Framework\Url\EncoderInterface;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Event\ManagerInterface as EventManager;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Magento\Catalog\Block\Product\Context as Productcontext;
use Magento\Catalog\Block\Product\ReviewRendererInterface;
use Magento\Framework\View\LayoutFactory;
use Psr\Log\LoggerInterface;
use Magento\Framework\Pricing\Helper\Data;
use Magento\Framework\Data\Form\FormKey;
use DateTime;
use Exception;

class Slider extends Template implements IdentityInterface
{
    /** {@inheritDoc} */
    protected $_template = 'Magelearn_Slider::slider.phtml';

    public const CACHE_TAG           = 'MAGELEARN_SLIDER';
    public const DEFAULT_TEMPLATE_ID = 1;
    public const DISABLED            = 0;
    public const ENABLED             = 1;

    /** @var ConfigHelper $helper */
    private $helper;

    /** @var SliderManagementInterface $sliderSlides */
    private $sliderSlides;

    /** @var SlideInterface[]|null $slides */
    private $slides = null;

    /**
     * @var FileInfo
     */
    private $fileInfo;
    
    /** @var Escaper */
    protected $escaper;
    
    /** @var EventManager */
    private $eventManager;
    
    /** @var ProductRepository */
    private $productRepository;
    
    protected $_productImageHelper;
    
    /**
     * @var ReviewRendererInterface
     */
    protected $reviewRenderer;

    /**
     * @var RendererList
     */
    private $rendererListBlock;
    
    /**
     * @var EncoderInterface|null
     */
    private $urlEncoder;
    
    /**
     * @var LayoutFactory
     */
    private $layoutFactory;
    
    protected $logger;
    
    protected $_priceHelper;
    protected $_formKey;
    
    /**
     * @var \Magento\Checkout\Helper\Cart
     */
    protected $_cartHelper;
    
    /**
     * @var \Magento\Wishlist\Helper\Data
     */
    protected $_wishlistHelper;
    
    /**
     * @var \Magento\Catalog\Helper\Product\Compare
     */
    protected $_compareProduct;

    /**
     * Slider constructor.
     *
     * @param Context $context
     * @param SliderManagementInterface $sliderSlides
     * @param ConfigHelper $helper
     * @param FileInfo $fileInfo
     * @param EventManager $eventManager
     * @param ProductRepository $productRepository
     * @param Image $productImageHelper
     * @param Escaper $escaper
     * @param Data $priceHelper
     * @param FormKey $formKey
     * @param LoggerInterface $logger
     * @param EncoderInterface|null $urlEncoder
     * @param LayoutFactory|null $layoutFactory
     * @param array $data
     */
    public function __construct(
        Context $context,
        Productcontext $productcontext,
        SliderManagementInterface $sliderSlides,
        ConfigHelper $helper,
        FileInfo $fileInfo,
        EventManager $eventManager,
        ProductRepository $productRepository,
        Image $productImageHelper,
        Escaper $escaper,
        LoggerInterface $logger,
        Data $priceHelper,
        FormKey $formKey,
        EncoderInterface $urlEncoder = null,
        LayoutFactory $layoutFactory = null,
        array $data = []
    ) {
        parent::__construct($context, $data);
        $this->helper       = $helper;
        $this->_cartHelper = $productcontext->getCartHelper();
        $this->reviewRenderer = $productcontext->getReviewRenderer();
        $this->sliderSlides = $sliderSlides;
        $this->fileInfo = $fileInfo;
        $this->eventManager = $eventManager;
        $this->escaper = $escaper;
        $this->productRepository = $productRepository;
        $this->_productImageHelper = $productImageHelper;
        $this->logger = $logger;
        $this->_priceHelper = $priceHelper;
        $this->_formKey = $formKey;
        $this->_compareProduct = $productcontext->getCompareProduct();
        $this->_wishlistHelper = $productcontext->getWishlistHelper();
        $this->urlEncoder = $urlEncoder ?: ObjectManager::getInstance()->get(EncoderInterface::class);
        $this->layoutFactory = $layoutFactory ?: ObjectManager::getInstance()->get(LayoutFactory::class);
    }

    /**
     * Allow you to clear all sliders cache just using the main tag, or each slider by it's id.
     *
     * @return array|string[]
     */
    public function getIdentities()
    {
        return [static::CACHE_TAG, static::CACHE_TAG . '_' . $this->getSliderId()];
    }

    /**
     * Returns slider_id passed in block data. If not present returns default id.
     */
    public function getSliderId()
    {
        return $this->getData('slider_id') ?? self::DEFAULT_TEMPLATE_ID;
    }

    /**
     * @return SlideInterface[]
     */
    public function getSlides(): array
    {
        if ($this->slides === null) {
            $this->slides = $this->sliderSlides->getSlides((int)$this->getSliderId(), true, true);
        }

        return $this->slides;
    }

    /**
     * @param $path
     *
     * @return string
     */
    public function getFileUrl($path): string
    {
        return $this->fileInfo->getImageUrl($path);
    }

    /**
     * @return bool
     */
    public function isEnabled(): bool
    {
        return $this->helper->isSliderEnabled();
    }
    
    /**
     * @return string
     */
    public function getSliderConfig(): string
    {
        return json_encode([
            'accessibility'  => $this->helper->getAccessibility(),
            'pauseOnFocus'   => $this->helper->isSlidePausedOnFocus(),
            'pauseOnHover'   => $this->helper->isSlidePausedOnHover(),
            'pauseOnDotsHover' => $this->helper->isSlidePauseOnDotsHover(),
            'arrows'         => $this->helper->isShowingArrowsEnabled(),
            'infinite'       => $this->helper->isInfiniteLoopEnabled(),
            'slidesToShow'   => $this->helper->getNumberOfSlidesToShow(),
            'slidesToScroll' => $this->helper->getNumberOfSlidesToShow(),
            'dots'           => $this->helper->isShowingDotsEnabled(),
            'autoplay'       => $this->helper->isAutoplayEnabled(),
            'autoplaySpeed'  => $this->helper->getAutoplaySpeed(),
            'lazyLoad'       => $this->helper->getLazyLoad(),
            'speed'          => $this->helper->getSpeed(),
            'cssEase'        => $this->helper->getCssEase()
        ]);
    }
    
    /**
     * @param string|null $src
     * @param int $videotype
     *
     * @return string
     */
    public function getVideoContent(?string $src, $videotype): string
    {
        if($src && null !== $src && ($videotype == 1 || $videotype == 2)) {
            $options = '?enablejsapi=1&playsinline=1&controls=0&fs=0&rel=0&showinfo=0&start=0&autoplay=0&loop=1';
            
            return '<iframe class="embed-player" src="' . $src . $options .
            '" frameborder="0" allow="accelerometer; autoplay; encrypted-media; ' .
            'gyroscope; picture-in-picture" allowfullscreen></iframe>';
        } elseif($src && null !== $src && $videotype == 3) {
            return '<video width="100%" height="100%" controls autoplay>
                      <source src="' . $src . '" type="video/mp4">
                    </video>';
        } else {
            return '';
        }
    }
    
    /**
     * @param null|string $topPosition
     * @param null|string $leftPosition
     *
     * @return string
     */
    public function getWrapperPosition(?string $topPosition, ?string $leftPosition): string
    {
        $html = '';
        
        if ($topPosition !== null) {
            $fromTop = $topPosition[0] === '-';
            if ($fromTop) {
                $side        = 'margin-bottom';
                $topPosition = substr($topPosition, 1);
            } else {
                $side = 'margin-top';
            }
            
            $html .= "$side: $topPosition;";
        }
        
        if ($leftPosition !== null) {
            $fromRight = $leftPosition[0] === '-';
            if ($fromRight) {
                $side         = 'margin-right';
                $leftPosition = substr($leftPosition, 1);
            } else {
                $side = 'margin-left';
            }
            
            $html .= "$side: $leftPosition;";
        }
        
        return empty($html) ? '' : 'style="' . $html . '"';
    }
    
    /**
     * @param string|null $foregroundColor
     * @param string|null $backgroundColor
     *
     * @return string
     */
    public function getStyles(?string $foregroundColor, ?string $backgroundColor): string
    {
        $html = '';
        
        if ($foregroundColor !== null) {
            $html .= "color: $foregroundColor; ";
        }
        
        if ($backgroundColor !== null) {
            $html .= "background-color: $backgroundColor; opacity: 0.8; ";
        }
        
        return empty($html) ? '' : 'style="' . $html . '"';
    }
    
    /**
     * Checks if counter conditions are met so we can display it. Conditions are:
     * counter must be enabled
     * counter date to must be set
     * if counter date from is set it must be before now
     *
     * @param SlideInterface $slide
     *
     * @return bool
     */
    public function displayCountdown(SlideInterface $slide): bool
    {
        $to = $slide->getCountdownDateTo();
        
        // counter must be enabled and `counter date to` must be set otherwise we can't display countdown
        if (!$slide->isActiveCountdown() || empty($to)) {
            return false;
        }
        
        $now = new DateTime();
        $now = $now->format('Y-m-d H:i:s');
        
        // if now is before `counter date from` then it can't be displayed (not yet)
        $from = $slide->getCountdownDateFrom();
        
        if (!empty($from) && $from > $now) {
            return false;
        }
        
        return true;
    }
    
    /**
     * @param SlideInterface $slide
     *
     * @return bool
     *
     * @throws Exception
     */
    public function displayDailyDealInformation(SlideInterface $slide): bool
    {
        if ($slide->hasShowDailyDeal() === false) {
            return false;
        }
        
        if ($slide->getDailyDealProductId() == 0) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Get Product by Id
     * @param int
     * @return \Magento\Catalog\Model\Product $product
     */
    public function getProduct(int $productId)
    {
        return $this->productRepository->getById($productId);
    }
    
    public function getProductBySku($sku)
    {
        return $this->productRepository->get($sku);
    }
    
    /**
     * Retrieve Product URL using UrlDataObject
     *
     * @param \Magento\Catalog\Model\Product $product
     * @param array $additional the route params
     * @return string
     */
    public function getProductUrl($product, $additional = [])
    {
        if ($this->hasProductUrl($product)) {
            if (!isset($additional['_escape'])) {
                $additional['_escape'] = true;
            }
            return $product->getUrlModel()->getUrl($product, $additional);
        }
        
        return '#';
    }
    
    /**
     * Check Product has URL
     *
     * @param \Magento\Catalog\Model\Product $product
     * @return bool
     */
    public function hasProductUrl($product)
    {
        if ($product->getVisibleInSiteVisibilities()) {
            return true;
        }
        if ($product->hasUrlDataObject()) {
            if (in_array($product->hasUrlDataObject()->getVisibility(), $product->getVisibleInSiteVisibilities())) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Schedule resize of the image
     * $width *or* $height can be null - in this case, lacking dimension will be calculated.
     *
     * @see \Magento\Catalog\Model\Product\Image
     * @param int $width
     * @param int $height
     * @return $this
     */
    public function resizeImage($product, $imageId, $width, $height = null)
    {
        $resizedImage = $this->_productImageHelper
                           ->init($product, $imageId)
                           ->constrainOnly(TRUE)
                           ->keepAspectRatio(TRUE)
                           ->keepTransparency(TRUE)
                           ->keepFrame(FALSE)
                           ->resize($width, $height);
        return $resizedImage;
    }
    
    /**
     * @inheritdoc
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    public function getProductPriceHtml(
        Product $product,
        $priceType = null,
        $renderZone = \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST,
        array $arguments = []
        ) {
            if (!isset($arguments['zone'])) {
                $arguments['zone'] = $renderZone;
            }
            $arguments['price_id'] = isset($arguments['price_id'])
            ? $arguments['price_id']
            : 'old-price-' . $product->getId() . '-' . $priceType;
            $arguments['include_container'] = isset($arguments['include_container'])
            ? $arguments['include_container']
            : true;
            $arguments['display_minimal_price'] = isset($arguments['display_minimal_price'])
            ? $arguments['display_minimal_price']
            : true;
            
            /** @var \Magento\Framework\Pricing\Render $priceRender */
            $priceRender = $this->getLayout()->getBlock('product.price.render.default');
            if (!$priceRender) {
                $priceRender = $this->getLayout()->createBlock(
                    \Magento\Framework\Pricing\Render::class,
                    'product.price.render.default',
                    ['data' => ['price_render_handle' => 'catalog_product_prices']]
                    );
            }
            
            $price = $priceRender->render(
                FinalPrice::PRICE_CODE,
                $product,
                $arguments
                );
            
            return $price;
    }
    
    /**
     * @inheritdoc
     */
    protected function getDetailsRendererList()
    {
        if (empty($this->rendererListBlock)) {
            /** @var $layout LayoutInterface */
            $layout = $this->layoutFactory->create(['cacheable' => false]);
            $layout->getUpdate()->addHandle('catalog_widget_product_list')->load();
            $layout->generateXml();
            $layout->generateElements();
            
            $this->rendererListBlock = $layout->getBlock('category.product.type.widget.details.renderers');
        }
        return $this->rendererListBlock;
    }
    
    /**
     * Get post parameters.
     *
     * @param Product $product
     * @return array
     */
    public function getAddToCartPostParams(Product $product)
    {
        $url = $this->getAddToCartUrl($product);
        return [
            'action' => $url,
            'data' => [
                'product' => $product->getEntityId(),
                ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlEncoder->encode($url),
            ]
        ];
    }
    
    public function getAddToCartUrl($product, $additional = [])
    {
        if (!$product->getTypeInstance()->isPossibleBuyFromList($product)) {
            if (!isset($additional['_escape'])) {
                $additional['_escape'] = true;
            }
            if (!isset($additional['_query'])) {
                $additional['_query'] = [];
            }
            $additional['_query']['options'] = 'cart';
            
            return $this->getProductUrl($product, $additional);
        }
        return $this->_cartHelper->getAddUrl($product, $additional);
    }
    public function getFormattedCurrency($price)
    {
        return $this->_priceHelper->currency($price, true, false);
    }
    public function getFormKey()
    {
        return $this->_formKey->getFormKey();
    }
    
    /**
     * Retrieve add to wishlist params
     *
     * @param \Magento\Catalog\Model\Product $product
     * @return string
     */
    public function getAddToWishlistParams($product)
    {
        return $this->_wishlistHelper->getAddParams($product);
    }
    
    /**
     * Retrieve Add Product to Compare Products List URL
     *
     * @return string
     */
    public function getAddToCompareUrl()
    {
        return $this->_compareProduct->getAddUrl();
    }
    
    /**
     * Get product reviews summary
     *
     * @param \Magento\Catalog\Model\Product $product
     * @param bool $templateType
     * @param bool $displayIfNoReviews
     * @return string
     */
    public function getReviewsSummaryHtml(
        \Magento\Catalog\Model\Product $product,
        $templateType = false,
        $displayIfNoReviews = false
        ) {
            return $this->reviewRenderer->getReviewsSummaryHtml($product, $templateType, $displayIfNoReviews);
    }
}

As per highlighted code above add helper file at Helper/Config.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\RequestInterface;
use Magento\Store\Model\ScopeInterface;

class Config extends AbstractHelper
{
    private const XML_PATH_MODULE_ENABLED   = 'magelearn_slider/general/is_enabled';
    private const XML_PATH_AUTOPLAY         = 'magelearn_slider/display_settings/autoplay';
    private const XML_PATH_ACCESSIBILITY         = 'magelearn_slider/display_settings/accessibility';
    private const XML_PATH_AUTOPLAY_SPEED   = 'magelearn_slider/display_settings/autoplay_speed';
    private const XML_PATH_SPEED   = 'magelearn_slider/display_settings/speed';
    private const XML_PATH_PAUSE_ON_FOCUS   = 'magelearn_slider/display_settings/pause_on_focus';
    private const XML_PATH_PAUSE_ON_HOVER   = 'magelearn_slider/display_settings/pause_on_hover';
    private const XML_PATH_PAUSE_ON_DOTS_HOVER   = 'magelearn_slider/display_settings/pause_on_dots_hover';
    private const XML_PATH_SHOW_ARROWS      = 'magelearn_slider/display_settings/show_arrows';
    private const XML_PATH_INFINITE_LOOP    = 'magelearn_slider/display_settings/infinite_loop';
    private const XML_PATH_SHOW_DOTS        = 'magelearn_slider/display_settings/show_dots';
    private const XML_PATH_SLIDES_TO_SHOW   = 'magelearn_slider/display_settings/slides_to_show';
    private const XML_PATH_SLIDES_TO_SCROLL = 'magelearn_slider/display_settings/slides_to_scroll';
    private const XML_PATH_LAZY_LOAD = 'magelearn_slider/display_settings/lazy_load';
    private const XML_PATH_CSS_EASE = 'magelearn_slider/display_settings/css_ease';

    /** @var RequestInterface $request */
    private $request;

    /**
     * @param Context $context
     */
    public function __construct(
        Context $context
    ) {
        parent::__construct($context);
        $this->request = $context->getRequest();
    }

    /**
     * @return bool
     */
    public function isModuleEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_MODULE_ENABLED,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function getSliderId()
    {
        return (int) $this->request->getParam('slider_id');
    }

    /**
     * @return bool
     */
    public function isSliderEnabled(): bool
    {
        return $this->isModuleEnabled();
    }

    /**
     * @return bool
     */
    public function isAutoplayEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_AUTOPLAY,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function getAutoplaySpeed(): int
    {
        return (int) $this->scopeConfig->getValue(
            static::XML_PATH_AUTOPLAY_SPEED,
            ScopeInterface::SCOPE_STORE
        );
    }
    
    /**
     * @return int
     */
    public function getSpeed(): int
    {
        return (int) $this->scopeConfig->getValue(
            static::XML_PATH_SPEED,
            ScopeInterface::SCOPE_STORE
            );
    }

    /**
     * @return bool
     */
    public function isSlidePausedOnFocus(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_PAUSE_ON_FOCUS,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return bool
     */
    public function isSlidePausedOnHover(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_PAUSE_ON_HOVER,
            ScopeInterface::SCOPE_STORE
        );
    }
    
    /**
     * @return bool
     */
    public function isSlidePauseOnDotsHover(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_PAUSE_ON_DOTS_HOVER,
            ScopeInterface::SCOPE_STORE
            );
    }

    /**
     * @return bool
     */
    public function isShowingArrowsEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_SHOW_ARROWS,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return bool
     */
    public function isInfiniteLoopEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_INFINITE_LOOP,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return bool
     */
    public function isShowingDotsEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_SHOW_DOTS,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function getNumberOfSlidesToShow(): int
    {
        return (int) $this->scopeConfig->getValue(
            self::XML_PATH_SLIDES_TO_SHOW,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * @return int
     */
    public function getNumberOfSlidesToScroll(): int
    {
        return (int) $this->scopeConfig->getValue(
            self::XML_PATH_SLIDES_TO_SCROLL,
            ScopeInterface::SCOPE_STORE
        );
    }
    
    /**
     * @return string
     */
    public function getLazyLoad(): string
    {
        return $this->scopeConfig->getValue(
            self::XML_PATH_LAZY_LOAD,
            ScopeInterface::SCOPE_STORE
            );
    }
    
    /**
     * @return string
     */
    public function getCssEase(): string
    {
        return $this->scopeConfig->getValue(
            self::XML_PATH_CSS_EASE,
            ScopeInterface::SCOPE_STORE
            );
    }
    
    /**
     * @return bool
     */
    public function getAccessibility(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ACCESSIBILITY,
            ScopeInterface::SCOPE_STORE
            );
    }
}

Add template file at view/frontend/templates/slider.phtml

<?php
use Magelearn\Slider\Block\Slider;
use Magento\Framework\Escaper;
use Magento\Framework\App\Action\Action;

/**
 * @var Slider  $block
 * @var Escaper $escaper
 */
?>
<?php if ($block->isEnabled()): ?>
    <?php $slides = $block->getSlides(); ?>

    <?php if (count($slides) > 0): ?>
    <aside class="slick__maincontainer">
            <div class="slick__wrapper">
                <section class="lazy slider main-slider"
                         data-mage-init='{ "magelearn.slider": <?= /* @noEscape */ $block->getSliderConfig(); ?> }'>
                         <?php foreach ($slides as $i => $slide): ?>
                            <?php
                                $videocontent = '';
                                if ($slide->getVideo() != 0) {
                                    $videocontent = $block->getVideoContent($slide->getVideoContent(), $slide->getVideo());
                                }
                            ?>
                            <div class="slide
                            <?php if ($slide->getVideo() == 1 && $videocontent !== ''): ?>youtube-sound video
                            <?php elseif ($slide->getVideo() == 2 && $videocontent !== ''): ?>vimeo video
                            <?php elseif ($slide->getVideo() == 3 && $videocontent !== ''): ?>html video
                                <?php else: ?><?php endif; ?>"
                                 data-title="<?= $block->escapeHtmlAttr($slide->getTitle()); ?>"
                                 data-url="<?= $block->escapeUrl($slide->getUrl()); ?>"
                                 data-image="<?= $block->escapeUrl($block->getFileUrl($slide->getImageMedium())); ?>"
                                >
                                <?php if ($videocontent !== ''): ?>
                                    <?= /* @noEscape */ $block->getVideoContent($slide->getVideoContent(), $slide->getVideo()); ?>
                                <?php else: ?>
                                <div class="slide__img">
                                    <picture>
                                    <source data-lazy-srcset="<?= $block->escapeUrl($block->getFileUrl($slide->getImageMedium())); ?>"
                                            media="(min-width: 1024px)">
                                    <?php if ($slide->getImageSmall()): ?>
                                        <source srcset="<?= $block->getFileUrl($slide->getImageSmall()); ?>"
                                                media="(min-width: 480px)">
                                    <?php endif; ?>
                                    <?php if ($slide->getImageMobile()): ?>
                                        <source srcset="<?= $block->getFileUrl($slide->getImageMobile()); ?>"
                                                media="(min-width: 320px)">
                                    <?php endif; ?>
                                    <img class="slider__content slide-image"
                                         <?php if ($i == 0): ?>
                                             data-exclude_webp_lazyload="true"
                                             src="<?= $block->escapeUrl($block->getFileUrl($slide->getImageMedium())); ?>"
                                         <?php else: ?>
                                             data-lazy="<?= $block->escapeUrl($block->getFileUrl($slide->getImageMedium())); ?>"
                                         <?php endif; ?>
                                         alt="<?= $block->escapeHtmlAttr($slide->getTitle()); ?>"/>
                                     </picture>
                                </div>
                                <div class="slide__content">
                                    <div class="slide__content--headings">
                                       <h2 class="animated" data-animation-in="fadeInUp"><?= /* @noEscape */ $slide->getTitle(); ?></h2>
                                       <p class="animated" data-animation-in="fadeInUp" data-delay-in="0.3"><?= /* @noEscape */ $slide->getContent(); ?></p>
                                       <?php if ($block->displayCountdown($slide)): ?>
                                            <?php $date = new DateTime($slide->getCountdownDateTo()); ?>
                                            <?php $countdownColor = $slide->getCountdownColor() ?>
                                            <?php $countdownBackgroundColor = $slide->getCountdownBackgroundColor() ?>
                                            <?php $itemStyle = $block->getStyles($countdownColor, $countdownBackgroundColor); ?>
                                            <!-- timestamp * 1000 because it converts to the JavaScript -->
                                            <div id="countdowntimer" class="countdowntimer" <?= /* @noEscape */ $itemStyle ?>>
                                                <span class="future_date" data-mage-init='{"countdownTimerInit":
                                                {
                                                 "startDate": "<?php echo date('Y/m/d H:i:s'); ?>",
                                                 "dateAndTime": "<?php echo $slide->getCountdownDateTo(); ?>"}
                                                }'><span>
                                            </div>
                                        <?php endif; ?>
                                        <?php if ($block->displayDailyDealInformation($slide)): ?>
                                            <?php $dailyDealProductId = explode(",", $slide->getDailyDealProductId()); ?>
                                            <?php $dailyDealProductIdCount = count($dailyDealProductId); ?>
                                            <?php $dailyDealColor = $slide->getDailyDealColor() ?>
                                            <?php $dailyDealBackgroundColor = $slide->getDailyDealBackgroundColor() ?>
                                            <?php $itemStyle = $block->getStyles($dailyDealColor, $dailyDealBackgroundColor); ?>
                                            <?php if (is_array($dailyDealProductId) && count($dailyDealProductId) > 0): ?>
                                                <div class="daily_deal__container" <?= /* @noEscape */ $block->getWrapperPosition(
                                            $slide->getDailyDealTop(),
                                            $slide->getDailyDealLeft()
                                        ) ?>>
                                                    <?php
                                                    $type = 'widget-product-grid';
                                                    $mode = 'grid';
                                                    $image = 'new_products_content_widget_grid';
                                                    $showWishlist = false;
                                                    $showCompare = false;
                                                    $showCart = true;
                                                    $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::SHORT_VIEW;
                                                    $description = false;
                                                    ?>
                                                    <?php foreach ($dailyDealProductId as $productId): ?>
                                                    <?php if ($product = $block->getProduct($productId)): ?>
                                                    <div class="item" style="width: calc((100% - 8%) / <?= $dailyDealProductIdCount ?>);">
                                                        <a href="<?= $block->escapeUrl($block->getProductUrl($product)) ?>" class="product-item-photo" target="_blank">
                                                            <?php
                                                            $imageId = 'product_base_image';
                                                            $width = 100;
                                                            $height = 200;
                                                            $sku = $product->getSku();
                                                            $_product = $block->getProductBySku($sku);
                                                            $resizedImageUrl = $block->resizeImage($_product, 'product_base_image', $width, $height)->getUrl();
                                                            ?>
                                                            <img src="<?php echo $resizedImageUrl;?>" alt="<?= $block->escapeHtml($product->getName()) ?>" />
                                                        </a>
                                                        <div class="product-item-details">
                                                            <strong class="product-item-name">
                                                                <?= $block->escapeHtml($product->getName()) ?>
                                                            </strong>
                                                            <?php if ($templateType): ?>
                                                                <?= $block->getReviewsSummaryHtml($product, $templateType) ?>
                                                            <?php endif; ?>
                                                            <?= $block->getProductPriceHtml($product, $type) ?>
 
                                                            <?= $block->getProductDetailsHtml($product) ?>
                                                            <?php if ($showWishlist || $showCompare || $showCart): ?>
                                                                <div class="product-item-inner-checkout">
                                                                    <div class="product-item-actions">
                                                                        <?php if ($showCart): ?>
                                                                            <div class="actions-primary">
                                                                                <?php if ($product->isSaleable()): ?>
                                                                                    <?php $postParams = $block->getAddToCartPostParams($product); ?>
                                                                                    <form data-role="tocart-form" data-product-sku="<?= $block->escapeHtml($product->getSku()) ?>" action="<?= $block->escapeUrl($postParams['action']) ?>" method="post">
                                                                                        <input type="hidden" name="product" value="<?= $block->escapeHtmlAttr($postParams['data']['product']) ?>">
                                                                                        <input type="hidden" name="<?= /* @noEscape */ Action::PARAM_NAME_URL_ENCODED ?>" value="<?= /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>">
                                                                                        <input type="hidden" name="form_key" value="<?= /* @noEscape */ $block->getFormKey() ?>" />
                                                                                        <button type="submit"
                                                                                                title="<?= $block->escapeHtml(__('Add to Cart')) ?>"
                                                                                                class="action tocart primary">
                                                                                            <span><?= $block->escapeHtml(__('Add to Cart')) ?></span>
                                                                                        </button>
                                                                                    </form>
                                                                                <?php else: ?>
                                                                                    <?php if ($product->isAvailable()): ?>
                                                                                        <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div>
                                                                                    <?php else: ?>
                                                                                        <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div>
                                                                                    <?php endif; ?>
                                                                                <?php endif; ?>
                                                                            </div>
                                                                        <?php endif; ?>
                                                                        <?php if ($showWishlist || $showCompare): ?>
                                                                            <div class="actions-secondary" data-role="add-to-links">
                                                                                <?php if ($this->helper(\Magento\Wishlist\Helper\Data::class)->isAllow() && $showWishlist): ?>
                                                                                    <a href="#"
                                                                                       data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($product) ?>' class="action towishlist" data-action="add-to-wishlist" title="<?= $block->escapeHtmlAttr(__('Add to Wish List')) ?>">
                                                                                        <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span>
                                                                                    </a>
                                                                                <?php endif; ?>
                                                                                <?php if ($block->getAddToCompareUrl() && $showCompare): ?>
                                                                                    <?php $compareHelper = $this->helper(\Magento\Catalog\Helper\Product\Compare::class);?>
                                                                                    <a href="#" class="action tocompare" data-post='<?= /* @noEscape */ $compareHelper->getPostDataParams($product) ?>' title="<?= $block->escapeHtmlAttr(__('Add to Compare')) ?>">
                                                                                        <span><?= $block->escapeHtml(__('Add to Compare')) ?></span>
                                                                                    </a>
                                                                                <?php endif; ?>
                                                                            </div>
                                                                        <?php endif; ?>
                                                                    </div>
                                                                </div>
                                                            <?php endif; ?>
                                                        </div>
                                                    </div>
                                                    <?php endif ?>
                                                    <?php endforeach ?>
                                                </div>
                                            <?php endif ?>
                                        <?php endif ?>
                                    </div>
                                </div>
                                <?php endif; ?>
                            </div>
                         <?php endforeach; ?>
                </section>
            </div>
    </aside>
    <?php endif; ?>
<?php endif; ?>

Now add view/frontend/requirejs-config.js file to add necessary JS files.

var config = {
    "map": {
        "*": {
            "slick": "Magelearn_Slider/js/vendor/slick.min",
            "slickanimation": "Magelearn_Slider/js/vendor/slick-animation.min",
            "magelearn.slider": "Magelearn_Slider/js/magelearn.slider",
            "countdownTimer": "Magelearn_Slider/js/jquery.countdownTimer",
            "countdownTimerInit": "Magelearn_Slider/js/countdownTimerInit"
        }
    },
    "shim": {
        "magelearn.slider"    : ["slick","slickanimation"],
        "countdownTimer" : ["jquery"],
        "countdownTimerInit" : ["jquery","countdownTimer"]
    }
};

As per Highlighted code above add Slick and countdown timer JS files at view/frontend/web/js/vendor/slick.min.js and view/frontend/web/js/vendor/slick-animation.min.js and view/frontend/web/js/jquery.countdownTimer.js

To initialize the countdown timer add file at view/frontend/web/js/countdownTimerInit.js

define([
    'jquery',
    'countdownTimer'
    ], function ($) {
        "use strict";
        return function (config) {
            $(".future_date").countdowntimer({
                startDate : config.startDate,
                dateAndTime : config.dateAndTime,
                size : "lg",
                regexpMatchFormat: "([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})",
    //          regexpReplaceWith: "$1 days $2 hours $3 minutes $4 seconds REMAINING"        
                regexpReplaceWith: "$1<sup>days</sup> / $2<sup>hours</sup> / $3<sup>minutes</sup> / $4<sup>seconds</sup>"
            }); 
        }
});

And for Slider add file at view/frontend/web/js/magelearn.slider.js

define(
    [
    'jquery',
    'slick',
    'slickanimation',
    'jquery-ui-modules/widget',
    'matchMedia',
    'underscore'
    ], function ($) {
        "use strict";

        $.widget(
            'mage.magelearnSlider', {
                options: {},
                
                _create: function (options) {
                    this.initialize();
                },
                
                
                initialize: function () {
                    var slideWrapper = $('.main-slider'),
                        iframes = slideWrapper.find('.embed-player'),
                        lazyImages = slideWrapper.find('.slide-image'),
                        lazyCounter = 0,
                        self = this;

                    $(
                        function () {
                            // event listener for detecting that video has ended so slider must go to next slide and
                            window.addEventListener(
                                'message', function (event) {
                                    // if not youtube then do nothing
                                    if ("https://www.youtube.com" !== event.origin || undefined === event.data) {
                                        return;
                                    }

                                    try {
                                        var data = JSON.parse(event.data);

                                        // we are not interested in events other than onStateChange
                                        if ('onStateChange' !== data.event) {
                                            return;
                                        }

                                        var playingFinished = 0;

                                        if (data.info === playingFinished) {
                                            var slick = slideWrapper.slick;

                                            // this check is for loop if there is only one slide (video slide)
                                            if (slick.getSlideCount() === 1) {
                                                var currentSlide = slick.find(".slick-current");
                                                this.playVideo(currentSlide, slick);
                                            }

                                            slick.slickPlay();
                                            slick.slickNext();
                                        }
                                    } catch(e) {
                                    }
                                }
                            );
                            // init is fired after first initialization (by slick library)
                            $(slideWrapper).on(
                                'init', function (event, slick) {
                                    slick = $(slick.$slider);
                                    setTimeout(function(){
                                      self.playPauseVideo(slick,"play");
                                    }, 1000);
                                    self.resizePlayer(iframes, 16/9);
                                }
                            );

                            $(slideWrapper).on(
                                'beforeChange', function (event, slick) {
                                    slick = $(slick.$slider);
                                    self.playPauseVideo(slick,"pause");
                                }
                            );

                            $(slideWrapper).on(
                                'afterChange', function (event, slick) {
                                    slick = $(slick.$slider);
                                    self.playPauseVideo(slick,"play");
                                }
                            );

                            $(slideWrapper).on(
                                'lazyLoaded', function (event, slick, image, imageSource) {
                                    lazyCounter++;
                                    if (lazyCounter === lazyImages.length){
                                      lazyImages.addClass('show');
                                      // slideWrapper.slick("slickPlay");
                                    }
                                    $(event.currentTarget).find('source').each(function(i, source) {
                                        var $source = $(source);
                                        $source.attr('srcset', $source.data('lazy-srcset'));
                                    });
                                }
                            );
                            
                            $(slideWrapper).on(
                                'click', '.slick-slide', function () {
                                    var url = $(this).data('url');

                                    if ('' !== url) {
                                        window.location.href = $(this).data('url');
                                    }
                                }
                            );
                            
                            $(window).on(
                                'resize.slickVideoPlayer', function () {
                                    self.resizePlayer(iframes, 16/9);
                                }
                            );

                            // this is main entry for running slick library
                           var slick = $(slideWrapper).not('.slick-initialized');

                           slick.slick(self.options).slickAnimation();
                           
                           // iframes must be loaded before we can play the video
                            slick.find('iframe').on(
                                'load', function () {
                                    // this timeout is here because even if iframe was loaded then I got error on sending command to the player
                                    // seems like there was still something to load - maybe from youtube library
                                    var iframe = $(this);

                                    setTimeout(
                                        function () {
                                            iframe.addClass('loaded');
                                        }, 100
                                    );
                                }
                            );
                           
                        }
                    );
                },

                /**
                 * Sends command to the player (youtube / vimeo)
                 *
                 * @param player
                 * @param command
                 */
                postMessageToPlayer: function (player, command) {
                    if (player === null || command === null) {
                        return;
                    }

                    player.contentWindow.postMessage(JSON.stringify(command), '*');
                },

                /**
                 * Checks if slide is video (by looking for iframe)
                 *
                 * @param slide
                 *
                 * @return {*}
                 */
                isVideo: function (slide) {
                    return slide.find('iframe').length;
                },

                /**
                 * Play video from provided slide. Slick object is needed to stop slider from changin slides.
                 *
                 * @param slide
                 * @param slick
                 */
                playVideo: function (slide, slick) {
                    slick.slickPause();

                    var iframe = slide.find('iframe');

                    if (iframe.hasClass('loaded')) {
                        // youtube api requires frame with id
                        iframe.prop('id', 'api_youtube_player');
                        this.sendPlayCommandToIframe(slide, iframe.get(0));
                    } else {
                        // wait till iframe is loaded, loading iframes is done by another piece of code (just after starting slick - bottom ot the code)
                        setTimeout(
                            function () {
                                this.playVideo(slide, slick);
                            }, 100
                        );
                    }
                },

                /**
                 * @param slide
                 * @param iframe
                 */
                sendPlayCommandToIframe: function (slide, iframe) {
                    // find kind of video
                    if (slide.hasClass('youtube')) {
                        // inform iframe with youtube that you want to listen messages from it
                        this.postMessageToPlayer(
                            iframe, {
                                event: "listening",
                                id: "api_youtube_player",
                                channel: "widget"
                            }
                        );

                        // enable more detailed event name than only "infoDelivery" (data.event)
                        this.postMessageToPlayer(
                            iframe, {
                                event: "command",
                                func: "addEventListener",
                                args: ["onStateChange"],
                                id: "api_youtube_player",
                                channel: "widget"
                            }
                        );

                        this.postMessageToPlayer(
                            iframe, {
                                'event': 'command',
                                'func': 'mute'
                            }
                        );

                        this.postMessageToPlayer(
                            iframe, {
                                'event': 'command',
                                'func': 'playVideo'
                            }
                        );
                    } else if (slide.hasClass('vimeo')) {
                        var startTime = slide.data('video-start');

                        if ((startTime != null && startTime > 0 ) && !slide.hasClass('started')) {
                            slide.addClass('started');
                            this.postMessageToPlayer(
                                iframe, {
                                    'method': 'setCurrentTime',
                                    'value' : startTime
                                }
                            );
                        }

                        this.postMessageToPlayer(
                            iframe, {
                                'method': 'play',
                                'value' : 1
                            }
                        );
                    } else if (slide.hasClass('video')) {
                        var video = slide.children('video').get(0);

                        if (video != null) {
                            video.play();
                        }
                    }
                },

                // When the slide is changing
                playPauseVideo: function (slick, control){
                  var currentSlide, slideType, startTime, player, video;
                
                  currentSlide = slick.find(".slick-current");
                  console.log(currentSlide.attr("class"));
                  slideType = currentSlide.attr("class").split(" ")[1];
                  player = currentSlide.find("iframe").get(0);
                  startTime = currentSlide.data("video-start");
                
                  if (slideType === "vimeo") {
                    switch (control) {
                      case "play":
                        if ((startTime != null && startTime > 0 ) && !currentSlide.hasClass('started')) {
                          currentSlide.addClass('started');
                          this.postMessageToPlayer(player, {
                            "method": "setCurrentTime",
                            "value" : startTime
                          });
                        }
                        this.postMessageToPlayer(player, {
                          "method": "play",
                          "value" : 1
                        });
                        break;
                      case "pause":
                        this.postMessageToPlayer(player, {
                          "method": "pause",
                          "value": 1
                        });
                        break;
                    }
                  } else if (slideType === "youtube-sound") {
                    switch (control) {
                      case "play":
                        this.postMessageToPlayer(player, {
                          "event": "command",
                          // "func": "mute"
                        });
                        this.postMessageToPlayer(player, {
                          "event": "command",
                          "func": "playVideo"
                        });
                        break;
                      case "pause":
                        this.postMessageToPlayer(player, {
                          "event": "command",
                          "func": "pauseVideo"
                        });
                        break;
                    }
                  }  else if (slideType === "youtube") {
                    switch (control) {
                      case "play":
                        this.postMessageToPlayer(player, {
                          "event": "command",
                          "func": "mute"
                        });
                        this.postMessageToPlayer(player, {
                          "event": "command",
                          "func": "playVideo"
                        });
                        break;
                      case "pause":
                        this.postMessageToPlayer(player, {
                          "event": "command",
                          "func": "pauseVideo"
                        });
                        break;
                    }
                  } else if (slideType === "video" || slideType === "html") {
                    video = currentSlide.children("video").get(0);
                    if (video != null) {
                      if (control === "play"){
                        video.play();
                      } else {
                        video.pause();
                      }
                    }
                  }
                },

                // Resize player
                resizePlayer: function (iframes, ratio) {
                  if (!iframes[0]) return;
                  var win = $(".main-slider"),
                      width = win.width(),
                      playerWidth,
                      height = win.height(),
                      playerHeight,
                      ratio = ratio || 16/9;
                
                  iframes.each(function(){
                    var current = $(this);
                    if (width / ratio < height) {
                      playerWidth = Math.ceil(height * ratio);
                      current.width(playerWidth).height(height).css({
                        left: (width - playerWidth) / 2,
                         top: 0
                        });
                    } else {
                      playerHeight = Math.ceil(width / ratio);
                      current.width(width).height(playerHeight).css({
                        left: 0,
                        top: (height - playerHeight) / 2
                      });
                    }
                  });
                },
            }
        );

        return $.mage.magelearnSlider;
    }
);

To display the youtube and vimeo video data properly, we will add csp_whitelist.xml file at etc/csp_whitelist.xml

<?xml version="1.0"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd">
    <policies>
        <policy id="script-src">
            <values>
                <value id="fontawesome" type="host">*.fontawesome.com</value>
                <value id="cloudflare" type="host">*.cloudflare.com</value>
                <value id="google-analytics" type="host">*.google-analytics.com</value>
                <value id="gstatic" type="host">*.gstatic.com</value>
                <value id="twitter.com" type="host">*.twitter.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="style-src">
            <values>
                <value id="fontawesome" type="host">*.fontawesome.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="img-src">
            <values>
                <value id="cloudflare" type="host">*.cloudflare.com</value>
                <value id="klarna-base" type="host">*.klarna.com</value>
                <value id="vimeocdn" type="host">*.vimeocdn.com</value>
                <value id="youtube-img" type="host">*.ytimg.com</value>
                <value id="data" type="host">'self' data:</value>
                <value id="doubleclick" type="host">*.doubleclick.net</value>
                <value id="mastercard" type="host">*.mastercard.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="connect-src">
            <values>
                <value id="cloudflare" type="host">*.cloudflare.com</value>
                <value id="twitter.com" type="host">*.twitter.com</value>
                <value id="google-analytics" type="host">*.google-analytics.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="font-src">
            <values>
                <value id="googleapis" type="host">*.googleapis.com</value>
                <value id="twitter.com" type="host">*.twitter.com</value>
                <value id="gstatic" type="host">*.gstatic.com</value>
                <value id="cloudflare" type="host">*.cloudflare.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="frame-src">
            <values>
                <value id="youtube.com" type="host">*.youtube.com</value>
                <value id="twitter.com" type="host">*.twitter.com</value>
                <value id="vimeo.com" type="host">*.vimeo.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="media-src">
            <values>
                <value id="zopim" type="host">*.zopim.com</value>
                <value id="zopimio" type="host">*.zopim.io</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
        <policy id="form-action">
            <values>
                <value id="twitter.com" type="host">*.twitter.com</value>
                <value id="googleads.g.doubleclick.net" type="host">*.googleads.g.doubleclick.net</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>

Now we will add our Cron file to check the slide end date and disable the slide if its end date is gone.

Add file at etc/crontab.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="magelearn_slider_date_range_checker" instance="Magelearn\Slider\Cron\CheckDateRange" method="execute">
            <schedule>* * * * *</schedule>
        </job>
    </group>
</config>

Add cron file at Cron/CheckDateRange.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Cron;

use Magelearn\Slider\Model\Slide\DateRangeChecker;

class CheckDateRange
{
    /**
     * @var DateRangeChecker
     */
    private $dateRangeChecker;

    /**
     * @param DateRangeChecker $dateRangeChecker
     */
    public function __construct(
        DateRangeChecker $dateRangeChecker
    ) {
        $this->dateRangeChecker = $dateRangeChecker;
    }

    /**
     * Change slides is_active value by date and invalidate cache
     *
     * @return void
     */
    public function execute()
    {
        $this->dateRangeChecker->execute();
    }
}

As per highlighted code above add file at Model/Slide/DateRangeChecker.php

<?php

declare(strict_types=1);

namespace Magelearn\Slider\Model\Slide;

use DateTimeZone;
use Magelearn\Slider\Api\Data\SlideInterface;
use Magelearn\Slider\Api\SlideRepositoryInterface;
use Magelearn\Slider\Block\Slider;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\CacheInterface;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Indexer\CacheContext;
use Magento\Framework\Stdlib\DateTime;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;

class DateRangeChecker
{
    /**
     * @var SlideRepositoryInterface
     */
    private $slideRepository;

    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * @var FilterBuilder
     */
    private $filterBuilder;

    /**
     * @var TimezoneInterface
     */
    private $timezone;

    /**
     * @var CacheContext
     */
    private $cacheContext;

    /**
     * @var ManagerInterface
     */
    private $eventManager;

    /**
     * @var CacheInterface
     */
    private $cacheManager;

    /**
     * @param SlideRepositoryInterface $slideRepository
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param FilterBuilder $filterBuilder
     * @param TimezoneInterface $timezone
     * @param CacheContext $cacheContext
     * @param ManagerInterface $eventManager
     * @param CacheInterface $cacheManager
     */
    public function __construct(
        SlideRepositoryInterface $slideRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        FilterBuilder $filterBuilder,
        TimezoneInterface $timezone,
        CacheContext $cacheContext,
        ManagerInterface $eventManager,
        CacheInterface $cacheManager
    ) {
        $this->slideRepository = $slideRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->filterBuilder = $filterBuilder;
        $this->timezone = $timezone;
        $this->cacheContext = $cacheContext;
        $this->eventManager = $eventManager;
        $this->cacheManager = $cacheManager;
    }

    /**
     * Change slides is_active value by date and invalidate cache
     *
     * @return void
     */
    public function execute()
    {
        $sliderIdsActivated = $this->activateSlides();
        $sliderIdsDeactivated = $this->deactivateSlides();

        $this->cleanCache(array_unique(array_merge($sliderIdsActivated, $sliderIdsDeactivated)));
    }

    /**
     * Find slides with is_active set to 0, that should be visible based on dates range, and activate those
     *
     * @return array IDs of sliders that were modified
     */
    private function activateSlides(): array
    {
        $sliderIds = [];

        $this->searchCriteriaBuilder->addFilter(SlideInterface::IS_ACTIVE, 0);

        $currentDate = $this->timezone->date();
        $currentDate->setTimezone(
            new DateTimeZone('UTC')
        );
        $today = $currentDate->format(DateTime::DATETIME_PHP_FORMAT);

        $this->searchCriteriaBuilder->addFilter(SlideInterface::DATE_FROM, $today, 'lteq');

        // filter group with OR inside
        $filter = $this->filterBuilder->setField(SlideInterface::DATE_TO)
            ->setConditionType('gteq')
            ->setValue($today)
            ->create();

        $filter2 = $this->filterBuilder->setField(SlideInterface::DATE_TO)
            ->setConditionType('null')
            ->setValue(true)
            ->create();

        $this->searchCriteriaBuilder->addFilters([$filter, $filter2]);

        $slides = $this->slideRepository->getList($this->searchCriteriaBuilder->create());

        if ($slides->getTotalCount() === 0) {
            return $sliderIds;
        }

        /** @var SlideInterface $slide */
        foreach ($slides->getItems() as $slide) {
            $slide->setIsActive(true);
            $this->slideRepository->save($slide);

            $sliderIds[] = (int)$slide->getSliderId();
        }

        return $sliderIds;
    }

    /**
     * Find slides with is_active set to 1, that should not be visible based on dates range, and deactivate those
     *
     * @return array IDs of sliders that were modified
     */
    private function deactivateSlides(): array
    {
        $sliderIds = [];

        $this->searchCriteriaBuilder->addFilter(SlideInterface::IS_ACTIVE, 1);

        $currentDate = $this->timezone->date();
        $currentDate->setTimezone(
            new DateTimeZone('UTC')
        );
        $today = $currentDate->format(DateTime::DATETIME_PHP_FORMAT);

        $this->searchCriteriaBuilder->addFilter(SlideInterface::DATE_TO, $today, 'lteq');

        $slides = $this->slideRepository->getList($this->searchCriteriaBuilder->create());

        if ($slides->getTotalCount() === 0) {
            return $sliderIds;
        }

        /** @var SlideInterface $slide */
        foreach ($slides->getItems() as $slide) {
            $slide->setIsActive(false);
            $this->slideRepository->save($slide);

            $sliderIds[] = (int)$slide->getSliderId();
        }

        return $sliderIds;
    }

    /**
     * @param int[] $sliderIds
     */
    private function cleanCache(array $sliderIds): void
    {
        if (count($sliderIds) === 0) {
            return;
        }

        $this->cacheContext->registerEntities(Slider::CACHE_TAG, $sliderIds);

        $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]);
        $this->cacheManager->clean($this->cacheContext->getIdentities());
    }
}

Also add CSS file at view/frontend/web/css/source/_module.less

And some additional CSS and images file for admin at view/adminhtml/web/css/source/_module.less and view/adminhtml/web/images/slide.png

0 Comments On "Magento2 homepage banner and product slider extension with countdown timer"

Back To Top