Slider Banner Magento
Magento

Magento 2 Banner Slider Module Tutorial – Admin Grid & Image Upload

Chapter 1: Magento 2 Front Banner Module – Complete Project Structure

In this comprehensive tutorial series, we will build a complete Magento 2 module called FrontBanner.
This module provides full banner management functionality including admin grid, form, image upload,
and frontend slider display. The module follows Magento 2 best practices and coding standards.

Complete Module Features

  • Complete admin grid with banner listing
  • Add/Edit banner functionality with image upload
  • Image preview in admin grid
  • Delete banner functionality
  • AJAX image upload
  • Frontend banner slider
  • ACL permissions for admin access
  • Database schema with declarative setup

Final Project Structure

Below is the complete folder structure of the FrontBanner module as implemented in Acesoftech/Frontendbanner.


C:.
│   registration.php
│
├───Block
│   │   BannerSlider.php
│   │
│   └───Adminhtml
│       │   Banner.php
│       │
│       └───Banner
│           │   Edit.php
│           │   Grid.php
│           │
│           ├───Edit
│           │       Form.php
│           │
│           └───Renderer
│                   Image.php
│
├───Controller
│   └───Adminhtml
│       ├───Banner
│       │       Delete.php
│       │       Edit.php
│       │       Index.php
│       │       NewAction.php
│       │       Save.php
│       │       Upload.php
│       │
│       ├───Hello
│       │       World.php
│       │
│       ├───Index
│       │       Index.php
│       │
│       ├───Ping
│       │       Index.php
│       │
│       └───Test
│               Index.php
│
├───etc
│   │   acl.xml
│   │   db_schema.xml
│   │   module.xml
│   │
│   └───adminhtml
│           menu.xml
│           routes.xml
│
├───Model
│   │   Banner.php
│   │   ImageUploader.php
│   │
│   └───ResourceModel
│       │   Banner.php
│       │
│       └───Banner
│               Collection.php
│
├───Setup
│       InstallData.php
│
└───view
    ├───adminhtml
    │   └───layout
    │           frontbanner_banner_edit.xml
    │           frontbanner_banner_index.xml
    │
    └───frontend
        ├───layout
        │       cms_index_index.xml
        │
        └───templates
                slider.phtml
    

Chapter 2: Module Registration and Configuration

2.1 registration.php

Location: app/code/FrontBanner/FrontBanner/registration.php


<?php
/**
 * Copyright © Acesoftech Pvt. Ltd. All rights reserved.
 */
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'FrontBanner_FrontBanner',
    __DIR__
);
    

2.2 etc/module.xml

Location: app/code/FrontBanner/FrontBanner/etc/module.xml


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="FrontBanner_FrontBanner" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Backend"/>
            <module name="Magento_Cms"/>
        </sequence>
    </module>
</config>
    

Chapter 3: Admin Routes and Menu Configuration

3.1 etc/adminhtml/routes.xml

Location: app/code/FrontBanner/FrontBanner/etc/adminhtml/routes.xml


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

3.2 etc/adminhtml/menu.xml

Location: app/code/FrontBanner/FrontBanner/etc/adminhtml/menu.xml


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="FrontBanner_FrontBanner::banner" 
             title="Banner Manager" 
             module="FrontBanner_FrontBanner" 
             sortOrder="10" 
             parent="Magento_Backend::content" 
             action="frontbanner/banner/index" 
             resource="FrontBanner_FrontBanner::banner"/>
    </menu>
</config>
    

3.3 etc/acl.xml

Location: app/code/FrontBanner/FrontBanner/etc/acl.xml


<?xml version="1.0"?>
<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="FrontBanner_FrontBanner::banner" 
                          title="Banner Manager" 
                          sortOrder="10" />
            </resource>
        </resources>
    </acl>
</config>
    

Chapter 4: Database Schema

4.1 etc/db_schema.xml

Location: app/code/FrontBanner/FrontBanner/etc/db_schema.xml


<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="frontbanner_banner" resource="default" engine="innodb" 
           comment="Front Banner Table">
        <column xsi:type="int" name="banner_id" padding="10" unsigned="true" 
                nullable="false" identity="true" comment="Banner ID"/>
        <column xsi:type="varchar" name="title" nullable="false" length="255" 
                comment="Banner Title"/>
        <column xsi:type="varchar" name="image" nullable="true" length="255" 
                comment="Banner Image"/>
        <column xsi:type="text" name="description" nullable="true" 
                comment="Banner Description"/>
        <column xsi:type="varchar" name="link" nullable="true" length="255" 
                comment="Banner Link"/>
        <column xsi:type="smallint" name="status" nullable="false" default="1" 
                comment="Banner Status"/>
        <column xsi:type="int" name="sort_order" nullable="false" default="0" 
                comment="Sort Order"/>
        <column xsi:type="timestamp" name="created_at" nullable="false" 
                default="CURRENT_TIMESTAMP" comment="Created At"/>
        <column xsi:type="timestamp" name="updated_at" nullable="false" 
                default="CURRENT_TIMESTAMP" on_update="true" comment="Updated At"/>
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="banner_id"/>
        </constraint>
        <index referenceId="FRONTBANNER_BANNER_STATUS">
            <column name="status"/>
        </index>
        <index referenceId="FRONTBANNER_BANNER_SORT_ORDER">
            <column name="sort_order"/>
        </index>
    </table>
</schema>
    

Chapter 5: Model Layer

5.1 Model/Banner.php

Location: app/code/FrontBanner/FrontBanner/Model/Banner.php


<?php
namespace FrontBanner\FrontBanner\Model;

use Magento\Framework\Model\AbstractModel;
use FrontBanner\FrontBanner\Model\ResourceModel\Banner as ResourceModel;

class Banner extends AbstractModel
{
    const STATUS_ENABLED = 1;
    const STATUS_DISABLED = 0;

    /**
     * @var string
     */
    protected $_eventPrefix = 'frontbanner_banner_model';

    /**
     * @var string
     */
    protected $_eventObject = 'banner';

    /**
     * Initialize resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init(ResourceModel::class);
    }

    /**
     * Get banner statuses
     *
     * @return array
     */
    public function getAvailableStatuses()
    {
        return [
            self::STATUS_ENABLED => __('Enabled'),
            self::STATUS_DISABLED => __('Disabled')
        ];
    }

    /**
     * Get image URL
     *
     * @return string
     */
    public function getImageUrl()
    {
        $image = $this->getImage();
        if ($image) {
            return $this->_getUrl()->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) 
                   . 'frontbanner/banner/' . $image;
        }
        return '';
    }

    /**
     * Get URL instance
     *
     * @return \Magento\Framework\UrlInterface
     */
    protected function _getUrl()
    {
        return \Magento\Framework\App\ObjectManager::getInstance()
            ->get(\Magento\Framework\UrlInterface::class);
    }
}
    

5.2 Model/ImageUploader.php

Location: app/code/FrontBanner/FrontBanner/Model/ImageUploader.php


<?php
namespace FrontBanner\FrontBanner\Model;

use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\MediaStorage\Model\File\UploaderFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Psr\Log\LoggerInterface;

class ImageUploader
{
    /**
     * @var string
     */
    const BASE_TMP_PATH = 'frontbanner/banner/tmp';

    /**
     * @var string
     */
    const BASE_PATH = 'frontbanner/banner';

    /**
     * @var string[]
     */
    protected $allowedExtensions = ['jpg', 'jpeg', 'gif', 'png'];

    /**
     * @var UploaderFactory
     */
    protected $uploaderFactory;

    /**
     * @var Filesystem\Directory\WriteInterface
     */
    protected $mediaDirectory;

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

    /**
     * @param Filesystem $filesystem
     * @param UploaderFactory $uploaderFactory
     * @param LoggerInterface $logger
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    public function __construct(
        Filesystem $filesystem,
        UploaderFactory $uploaderFactory,
        LoggerInterface $logger
    ) {
        $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
        $this->uploaderFactory = $uploaderFactory;
        $this->logger = $logger;
    }

    /**
     * Save file to temp media directory
     *
     * @param string $fileId
     * @return array
     * @throws LocalizedException
     */
    public function saveFileToTmpDir($fileId)
    {
        try {
            $this->createDirectoryIfNotExists(self::BASE_TMP_PATH);
            
            /** @var \Magento\MediaStorage\Model\File\Uploader $uploader */
            $uploader = $this->uploaderFactory->create(['fileId' => $fileId]);
            $uploader->setAllowedExtensions($this->allowedExtensions);
            $uploader->setAllowRenameFiles(true);
            
            $result = $uploader->save($this->mediaDirectory->getAbsolutePath(self::BASE_TMP_PATH));
            
            if (!$result) {
                throw new LocalizedException(__('File can not be saved to the destination folder.'));
            }
            
            unset($result['path']);
            
            return $result;
        } catch (\Exception $e) {
            $this->logger->critical($e);
            throw new LocalizedException(__('Something went wrong while saving the file.'));
        }
    }

    /**
     * Move file from tmp to permanent directory
     *
     * @param string $filename
     * @return string
     */
    public function moveFileFromTmp($filename)
    {
        $this->createDirectoryIfNotExists(self::BASE_PATH);
        
        $tmpFile = $this->getFilePath(self::BASE_TMP_PATH, $filename);
        $newFile = $this->getFilePath(self::BASE_PATH, $filename);
        
        if ($this->mediaDirectory->isExist($tmpFile)) {
            $this->mediaDirectory->renameFile($tmpFile, $newFile);
        }
        
        return $filename;
    }

    /**
     * Create directory if not exists
     *
     * @param string $path
     * @return void
     */
    protected function createDirectoryIfNotExists($path)
    {
        if (!$this->mediaDirectory->isExist($path)) {
            $this->mediaDirectory->create($path);
        }
    }

    /**
     * Get file path
     *
     * @param string $path
     * @param string $filename
     * @return string
     */
    protected function getFilePath($path, $filename)
    {
        return rtrim($path, '/') . '/' . ltrim($filename, '/');
    }
}
    

Chapter 6: Resource Model Layer

6.1 Model/ResourceModel/Banner.php

Location: app/code/FrontBanner/FrontBanner/Model/ResourceModel/Banner.php


<?php
namespace FrontBanner\FrontBanner\Model\ResourceModel;

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

class Banner extends AbstractDb
{
    /**
     * @var string
     */
    protected $_eventPrefix = 'frontbanner_banner_resource_model';

    /**
     * Initialize resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('frontbanner_banner', 'banner_id');
    }

    /**
     * Process banner data before saving
     *
     * @param \Magento\Framework\Model\AbstractModel $object
     * @return $this
     */
    protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
    {
        if ($object->isObjectNew()) {
            $object->setCreatedAt(date('Y-m-d H:i:s'));
        }
        $object->setUpdatedAt(date('Y-m-d H:i:s'));
        
        return parent::_beforeSave($object);
    }
}
    

6.2 Model/ResourceModel/Banner/Collection.php

Location: app/code/FrontBanner/FrontBanner/Model/ResourceModel/Banner/Collection.php


<?php
namespace FrontBanner\FrontBanner\Model\ResourceModel\Banner;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use FrontBanner\FrontBanner\Model\Banner as BannerModel;
use FrontBanner\FrontBanner\Model\ResourceModel\Banner as BannerResource;

class Collection extends AbstractCollection
{
    /**
     * @var string
     */
    protected $_idFieldName = 'banner_id';

    /**
     * @var string
     */
    protected $_eventPrefix = 'frontbanner_banner_collection';

    /**
     * @var string
     */
    protected $_eventObject = 'banner_collection';

    /**
     * Define resource model
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init(BannerModel::class, BannerResource::class);
    }

    /**
     * Add enabled filter
     *
     * @return $this
     */
    public function addEnabledFilter()
    {
        $this->addFieldToFilter('status', BannerModel::STATUS_ENABLED);
        return $this;
    }

    /**
     * Add sort order
     *
     * @param string $direction
     * @return $this
     */
    public function addSortOrder($direction = 'ASC')
    {
        $this->setOrder('sort_order', $direction);
        return $this;
    }
}
    

Chapter 7: Admin Controllers

7.1 Controller/Adminhtml/Banner/Index.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Banner/Index.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Banner;

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

class Index extends Action
{
    /**
     * @var PageFactory
     */
    protected $resultPageFactory;

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

    /**
     * Check the permission
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('FrontBanner_FrontBanner::banner');
    }

    /**
     * Index action
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        /** @var \Magento\Backend\Model\View\Result\Page $resultPage */
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('FrontBanner_FrontBanner::banner');
        $resultPage->addBreadcrumb(__('CMS'), __('CMS'));
        $resultPage->addBreadcrumb(__('Banner Manager'), __('Banner Manager'));
        $resultPage->getConfig()->getTitle()->prepend(__('Banner Manager'));
        
        return $resultPage;
    }
}
    

7.2 Controller/Adminhtml/Banner/NewAction.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Banner/NewAction.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Banner;

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

class NewAction extends Action
{
    /**
     * @var PageFactory
     */
    protected $resultPageFactory;

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

    /**
     * Check the permission
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('FrontBanner_FrontBanner::banner');
    }

    /**
     * New action
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('FrontBanner_FrontBanner::banner');
        $resultPage->addBreadcrumb(__('CMS'), __('CMS'));
        $resultPage->addBreadcrumb(__('Banner Manager'), __('Banner Manager'));
        $resultPage->getConfig()->getTitle()->prepend(__('New Banner'));
        
        return $resultPage;
    }
}
    

7.3 Controller/Adminhtml/Banner/Edit.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Banner/Edit.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Banner;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use FrontBanner\FrontBanner\Model\BannerFactory;
use Magento\Framework\Registry;

class Edit extends Action
{
    /**
     * @var PageFactory
     */
    protected $resultPageFactory;

    /**
     * @var BannerFactory
     */
    protected $bannerFactory;

    /**
     * @var Registry
     */
    protected $coreRegistry;

    /**
     * @param Context $context
     * @param PageFactory $resultPageFactory
     * @param BannerFactory $bannerFactory
     * @param Registry $coreRegistry
     */
    public function __construct(
        Context $context,
        PageFactory $resultPageFactory,
        BannerFactory $bannerFactory,
        Registry $coreRegistry
    ) {
        parent::__construct($context);
        $this->resultPageFactory = $resultPageFactory;
        $this->bannerFactory = $bannerFactory;
        $this->coreRegistry = $coreRegistry;
    }

    /**
     * Check the permission
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('FrontBanner_FrontBanner::banner');
    }

    /**
     * Edit action
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $id = $this->getRequest()->getParam('banner_id');
        $model = $this->bannerFactory->create();

        if ($id) {
            $model->load($id);
            if (!$model->getId()) {
                $this->messageManager->addErrorMessage(__('This banner no longer exists.'));
                $resultRedirect = $this->resultRedirectFactory->create();
                return $resultRedirect->setPath('*/*/');
            }
        }

        $this->coreRegistry->register('frontbanner_banner', $model);

        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('FrontBanner_FrontBanner::banner');
        $resultPage->addBreadcrumb(__('CMS'), __('CMS'));
        $resultPage->addBreadcrumb(__('Banner Manager'), __('Banner Manager'));
        $resultPage->getConfig()->getTitle()->prepend(
            $model->getId() ? __('Edit Banner: %1', $model->getTitle()) : __('New Banner')
        );

        return $resultPage;
    }
}
    

7.4 Controller/Adminhtml/Banner/Save.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Banner/Save.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Banner;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use FrontBanner\FrontBanner\Model\BannerFactory;
use FrontBanner\FrontBanner\Model\ImageUploader;
use Magento\Framework\Exception\LocalizedException;

class Save extends Action
{
    /**
     * @var BannerFactory
     */
    protected $bannerFactory;

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

    /**
     * @param Context $context
     * @param BannerFactory $bannerFactory
     * @param ImageUploader $imageUploader
     */
    public function __construct(
        Context $context,
        BannerFactory $bannerFactory,
        ImageUploader $imageUploader
    ) {
        parent::__construct($context);
        $this->bannerFactory = $bannerFactory;
        $this->imageUploader = $imageUploader;
    }

    /**
     * Check the permission
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('FrontBanner_FrontBanner::banner');
    }

    /**
     * Save action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $data = $this->getRequest()->getPostValue();
        $resultRedirect = $this->resultRedirectFactory->create();

        if ($data) {
            $id = $this->getRequest()->getParam('banner_id');
            $model = $this->bannerFactory->create();

            if ($id) {
                $model->load($id);
            }

            // Handle image upload
            if (isset($data['image']) && isset($data['image']['value'])) {
                $data['image'] = $data['image']['value'];
            } elseif (isset($data['image']) && is_array($data['image'])) {
                if (!empty($data['image'][0]['name']) && !empty($data['image'][0]['tmp_name'])) {
                    $data['image'] = $this->imageUploader->moveFileFromTmp($data['image'][0]['name']);
                } else {
                    unset($data['image']);
                }
            } else {
                if (isset($data['image']) && !$data['image'][0]['name']) {
                    $data['image'] = '';
                }
            }

            $model->setData($data);

            try {
                $model->save();
                $this->messageManager->addSuccessMessage(__('You saved the banner.'));
                
                if ($this->getRequest()->getParam('back')) {
                    return $resultRedirect->setPath('*/*/edit', ['banner_id' => $model->getId()]);
                }
                return $resultRedirect->setPath('*/*/');
            } catch (LocalizedException $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
            } catch (\Exception $e) {
                $this->messageManager->addErrorMessage(__('Something went wrong while saving the banner.'));
            }

            return $resultRedirect->setPath('*/*/edit', ['banner_id' => $id]);
        }

        return $resultRedirect->setPath('*/*/');
    }
}
    

7.5 Controller/Adminhtml/Banner/Delete.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Banner/Delete.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Banner;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use FrontBanner\FrontBanner\Model\BannerFactory;

class Delete extends Action
{
    /**
     * @var BannerFactory
     */
    protected $bannerFactory;

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

    /**
     * Check the permission
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('FrontBanner_FrontBanner::banner');
    }

    /**
     * Delete action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $id = $this->getRequest()->getParam('banner_id');
        $resultRedirect = $this->resultRedirectFactory->create();

        if ($id) {
            try {
                $model = $this->bannerFactory->create();
                $model->load($id);
                $model->delete();
                
                $this->messageManager->addSuccessMessage(__('You deleted the banner.'));
                return $resultRedirect->setPath('*/*/');
            } catch (\Exception $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
                return $resultRedirect->setPath('*/*/edit', ['banner_id' => $id]);
            }
        }

        $this->messageManager->addErrorMessage(__('We can\'t find a banner to delete.'));
        return $resultRedirect->setPath('*/*/');
    }
}
    

7.6 Controller/Adminhtml/Banner/Upload.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Banner/Upload.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Banner;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use FrontBanner\FrontBanner\Model\ImageUploader;
use Magento\Framework\Controller\ResultFactory;

class Upload extends Action
{
    /**
     * @var ImageUploader
     */
    protected $imageUploader;

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

    /**
     * Check the permission
     *
     * @return bool
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('FrontBanner_FrontBanner::banner');
    }

    /**
     * Upload action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        try {
            $result = $this->imageUploader->saveFileToTmpDir('image');
            $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->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
    }
}
    

Chapter 8: Admin Blocks

8.1 Block/Adminhtml/Banner.php

Location: app/code/FrontBanner/FrontBanner/Block/Adminhtml/Banner.php


<?php
namespace FrontBanner\FrontBanner\Block\Adminhtml;

use Magento\Backend\Block\Widget\Grid\Container;

class Banner extends Container
{
    /**
     * Constructor
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_controller = 'adminhtml_banner';
        $this->_blockGroup = 'FrontBanner_FrontBanner';
        $this->_headerText = __('Banner Manager');
        $this->_addButtonLabel = __('Add New Banner');
        
        parent::_construct();
    }
}
    

8.2 Block/Adminhtml/Banner/Grid.php

Location: app/code/FrontBanner/FrontBanner/Block/Adminhtml/Banner/Grid.php


<?php
namespace FrontBanner\FrontBanner\Block\Adminhtml\Banner;

use Magento\Backend\Block\Widget\Grid\Extended;
use Magento\Backend\Block\Template\Context;
use Magento\Backend\Helper\Data;
use FrontBanner\FrontBanner\Model\ResourceModel\Banner\CollectionFactory;
use FrontBanner\FrontBanner\Model\Banner;

class Grid extends Extended
{
    /**
     * @var CollectionFactory
     */
    protected $collectionFactory;

    /**
     * @param Context $context
     * @param Data $backendHelper
     * @param CollectionFactory $collectionFactory
     * @param array $data
     */
    public function __construct(
        Context $context,
        Data $backendHelper,
        CollectionFactory $collectionFactory,
        array $data = []
    ) {
        $this->collectionFactory = $collectionFactory;
        parent::__construct($context, $backendHelper, $data);
    }

    /**
     * Initialize grid
     *
     * @return void
     */
    protected function _construct()
    {
        parent::_construct();
        $this->setId('bannerGrid');
        $this->setDefaultSort('banner_id');
        $this->setDefaultDir('DESC');
        $this->setSaveParametersInSession(true);
        $this->setUseAjax(true);
    }

    /**
     * Prepare collection
     *
     * @return \Magento\Backend\Block\Widget\Grid\Extended
     */
    protected function _prepareCollection()
    {
        $collection = $this->collectionFactory->create();
        $this->setCollection($collection);
        
        return parent::_prepareCollection();
    }

    /**
     * Prepare columns
     *
     * @return \Magento\Backend\Block\Widget\Grid\Extended
     */
    protected function _prepareColumns()
    {
        $this->addColumn('banner_id', [
            'header' => __('ID'),
            'index' => 'banner_id',
            'type' => 'number',
            'width' => '50px'
        ]);

        $this->addColumn('title', [
            'header' => __('Title'),
            'index' => 'title',
            'class' => 'xxx',
            'width' => '200px'
        ]);

        $this->addColumn('image', [
            'header' => __('Image'),
            'class' => 'xxx',
            'width' => '150px',
            'filter' => false,
            'renderer' => \FrontBanner\FrontBanner\Block\Adminhtml\Banner\Renderer\Image::class
        ]);

        $this->addColumn('description', [
            'header' => __('Description'),
            'index' => 'description',
            'class' => 'xxx',
            'width' => '300px'
        ]);

        $this->addColumn('link', [
            'header' => __('Link'),
            'index' => 'link',
            'class' => 'xxx',
            'width' => '200px'
        ]);

        $this->addColumn('sort_order', [
            'header' => __('Sort Order'),
            'index' => 'sort_order',
            'type' => 'number',
            'width' => '80px'
        ]);

        $this->addColumn('status', [
            'header' => __('Status'),
            'index' => 'status',
            'type' => 'options',
            'options' => [
                Banner::STATUS_ENABLED => __('Enabled'),
                Banner::STATUS_DISABLED => __('Disabled')
            ],
            'width' => '100px'
        ]);

        $this->addColumn('created_at', [
            'header' => __('Created At'),
            'index' => 'created_at',
            'type' => 'datetime',
            'width' => '170px'
        ]);

        $this->addColumn('updated_at', [
            'header' => __('Updated At'),
            'index' => 'updated_at',
            'type' => 'datetime',
            'width' => '170px'
        ]);

        $this->addColumn('action', [
            'header' => __('Action'),
            'width' => '100px',
            'type' => 'action',
            'getter' => 'getId',
            'actions' => [
                [
                    'caption' => __('Edit'),
                    'url' => [
                        'base' => '*/*/edit'
                    ],
                    'field' => 'banner_id'
                ]
            ],
            'filter' => false,
            'sortable' => false,
            'index' => 'stores',
            'is_system' => true
        ]);

        return parent::_prepareColumns();
    }

    /**
     * Get row url
     *
     * @param \Magento\Framework\DataObject $row
     * @return string
     */
    public function getRowUrl($row)
    {
        return $this->getUrl('*/*/edit', ['banner_id' => $row->getId()]);
    }

    /**
     * Get grid url
     *
     * @return string
     */
    public function getGridUrl()
    {
        return $this->getUrl('*/*/grid', ['_current' => true]);
    }
}
    

8.3 Block/Adminhtml/Banner/Renderer/Image.php

Location: app/code/FrontBanner/FrontBanner/Block/Adminhtml/Banner/Renderer/Image.php


<?php
namespace FrontBanner\FrontBanner\Block\Adminhtml\Banner\Renderer;

use Magento\Framework\DataObject;
use Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer;

class Image extends AbstractRenderer
{
    /**
     * @var \Magento\Framework\UrlInterface
     */
    protected $urlBuilder;

    /**
     * @param \Magento\Backend\Block\Context $context
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Context $context,
        array $data = []
    ) {
        $this->urlBuilder = $context->getUrlBuilder();
        parent::__construct($context, $data);
    }

    /**
     * Render image
     *
     * @param DataObject $row
     * @return string
     */
    public function render(DataObject $row)
    {
        $image = $row->getData($this->getColumn()->getIndex());
        
        if ($image) {
            $imageUrl = $this->urlBuilder->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) 
                       . 'frontbanner/banner/' . $image;
            return '<img src="' . $imageUrl . '" width="100" height="50" />';
        }
        
        return '';
    }
}
    

8.4 Block/Adminhtml/Banner/Edit.php

Location: app/code/FrontBanner/FrontBanner/Block/Adminhtml/Banner/Edit.php


<?php
namespace FrontBanner\FrontBanner\Block\Adminhtml\Banner;

use Magento\Backend\Block\Widget\Form\Container;
use Magento\Backend\Block\Widget\Context;
use Magento\Framework\Registry;

class Edit extends Container
{
    /**
     * @var Registry
     */
    protected $coreRegistry;

    /**
     * @param Context $context
     * @param Registry $registry
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        array $data = []
    ) {
        $this->coreRegistry = $registry;
        parent::__construct($context, $data);
    }

    /**
     * Initialize banner edit block
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_objectId = 'banner_id';
        $this->_controller = 'adminhtml_banner';
        $this->_blockGroup = 'FrontBanner_FrontBanner';

        parent::_construct();

        $this->buttonList->update('save', 'label', __('Save Banner'));
        $this->buttonList->update('delete', 'label', __('Delete Banner'));

        $this->buttonList->add(
            'saveandcontinue',
            [
                'label' => __('Save and Continue Edit'),
                'class' => 'save',
                'data_attribute' => [
                    'mage-init' => [
                        'button' => [
                            'event' => 'saveAndContinueEdit',
                            'target' => '#edit_form'
                        ]
                    ]
                ]
            ],
            -100
        );
    }

    /**
     * Get header text
     *
     * @return \Magento\Framework\Phrase
     */
    public function getHeaderText()
    {
        $banner = $this->coreRegistry->registry('frontbanner_banner');
        if ($banner && $banner->getId()) {
            return __("Edit Banner '%1'", $this->escapeHtml($banner->getTitle()));
        }
        return __('New Banner');
    }
}
    

8.5 Block/Adminhtml/Banner/Edit/Form.php

Location: app/code/FrontBanner/FrontBanner/Block/Adminhtml/Banner/Edit/Form.php


<?php
namespace FrontBanner\FrontBanner\Block\Adminhtml\Banner\Edit;

use Magento\Backend\Block\Widget\Form\Generic;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Registry;
use Magento\Framework\Data\FormFactory;
use FrontBanner\FrontBanner\Model\Banner;

class Form extends Generic
{
    /**
     * @var Banner
     */
    protected $bannerModel;

    /**
     * @param Context $context
     * @param Registry $registry
     * @param FormFactory $formFactory
     * @param Banner $bannerModel
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        FormFactory $formFactory,
        Banner $bannerModel,
        array $data = []
    ) {
        $this->bannerModel = $bannerModel;
        parent::__construct($context, $registry, $formFactory, $data);
    }

    /**
     * Prepare form
     *
     * @return $this
     */
    protected function _prepareForm()
    {
        /** @var \FrontBanner\FrontBanner\Model\Banner $banner */
        $banner = $this->_coreRegistry->registry('frontbanner_banner');

        /** @var \Magento\Framework\Data\Form $form */
        $form = $this->_formFactory->create(
            [
                'data' => [
                    'id' => 'edit_form',
                    'action' => $this->getData('action'),
                    'method' => 'post',
                    'enctype' => 'multipart/form-data'
                ]
            ]
        );

        $fieldset = $form->addFieldset(
            'base_fieldset',
            ['legend' => __('Banner Information'), 'class' => 'fieldset-wide']
        );

        if ($banner && $banner->getId()) {
            $fieldset->addField(
                'banner_id',
                'hidden',
                ['name' => 'banner_id']
            );
        }

        $fieldset->addField(
            'title',
            'text',
            [
                'name' => 'title',
                'label' => __('Title'),
                'title' => __('Title'),
                'required' => true
            ]
        );

        $fieldset->addField(
            'description',
            'textarea',
            [
                'name' => 'description',
                'label' => __('Description'),
                'title' => __('Description'),
                'required' => false
            ]
        );

        $fieldset->addField(
            'link',
            'text',
            [
                'name' => 'link',
                'label' => __('Link URL'),
                'title' => __('Link URL'),
                'required' => false
            ]
        );

        $fieldset->addField(
            'sort_order',
            'text',
            [
                'name' => 'sort_order',
                'label' => __('Sort Order'),
                'title' => __('Sort Order'),
                'required' => false,
                'class' => 'validate-number'
            ]
        );

        $fieldset->addField(
            'status',
            'select',
            [
                'name' => 'status',
                'label' => __('Status'),
                'title' => __('Status'),
                'values' => $this->bannerModel->getAvailableStatuses(),
                'required' => true
            ]
        );

        $fieldset->addField(
            'image',
            'file',
            [
                'name' => 'image',
                'label' => __('Banner Image'),
                'title' => __('Banner Image'),
                'required' => false,
                'after_element_html' => $this->getImageHtml($banner)
            ]
        );

        $form->setValues($banner ? $banner->getData() : []);
        $form->setUseContainer(true);
        $this->setForm($form);

        return parent::_prepareForm();
    }

    /**
     * Get image HTML
     *
     * @param \FrontBanner\FrontBanner\Model\Banner $banner
     * @return string
     */
    protected function getImageHtml($banner)
    {
        if ($banner && $banner->getImage()) {
            $imageUrl = $this->_urlBuilder->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) 
                       . 'frontbanner/banner/' . $banner->getImage();
            return '<br/><img src="' . $imageUrl . '" width="200" height="100" />';
        }
        return '';
    }
}
    

Chapter 9: Frontend Blocks

9.1 Block/BannerSlider.php

Location: app/code/FrontBanner/FrontBanner/Block/BannerSlider.php


<?php
namespace FrontBanner\FrontBanner\Block;

use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use FrontBanner\FrontBanner\Model\ResourceModel\Banner\CollectionFactory;
use FrontBanner\FrontBanner\Model\Banner;

class BannerSlider extends Template
{
    /**
     * @var CollectionFactory
     */
    protected $collectionFactory;

    /**
     * @var \Magento\Framework\UrlInterface
     */
    protected $urlBuilder;

    /**
     * @param Context $context
     * @param CollectionFactory $collectionFactory
     * @param array $data
     */
    public function __construct(
        Context $context,
        CollectionFactory $collectionFactory,
        array $data = []
    ) {
        $this->collectionFactory = $collectionFactory;
        $this->urlBuilder = $context->getUrlBuilder();
        parent::__construct($context, $data);
    }

    /**
     * Get banner collection
     *
     * @return \FrontBanner\FrontBanner\Model\ResourceModel\Banner\Collection
     */
    public function getBanners()
    {
        $collection = $this->collectionFactory->create();
        $collection->addEnabledFilter()
                   ->addSortOrder()
                   ->setPageSize(10)
                   ->setCurPage(1);
        
        return $collection;
    }

    /**
     * Get banner image URL
     *
     * @param \FrontBanner\FrontBanner\Model\Banner $banner
     * @return string
     */
    public function getBannerImageUrl($banner)
    {
        if ($banner && $banner->getImage()) {
            return $this->urlBuilder->getBaseUrl(['_type' => \Magento\Framework\UrlInterface::URL_TYPE_MEDIA]) 
                   . 'frontbanner/banner/' . $banner->getImage();
        }
        return '';
    }

    /**
     * Get slider configuration
     *
     * @return string
     */
    public function getSliderConfig()
    {
        return json_encode([
            'autoplay' => true,
            'autoplaySpeed' => 5000,
            'dots' => true,
            'arrows' => true,
            'infinite' => true,
            'speed' => 500,
            'slidesToShow' => 1,
            'slidesToScroll' => 1
        ]);
    }
}
    

Chapter 10: Setup Scripts

10.1 Setup/InstallData.php

Location: app/code/FrontBanner/FrontBanner/Setup/InstallData.php


<?php
namespace FrontBanner\FrontBanner\Setup;

use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

class InstallData implements InstallDataInterface
{
    /**
     * @var EavSetupFactory
     */
    protected $eavSetupFactory;

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

    /**
     * Install data
     *
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        // Add sample data or additional setup here if needed
        
        $setup->endSetup();
    }
}
    

Chapter 11: Layout Files

11.1 view/adminhtml/layout/frontbanner_banner_index.xml

Location: app/code/FrontBanner/FrontBanner/view/adminhtml/layout/frontbanner_banner_index.xml


<?xml version="1.0"?>
<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">
            <block class="FrontBanner\FrontBanner\Block\Adminhtml\Banner" name="frontbanner_banner_grid"/>
        </referenceContainer>
    </body>
</page>
    

11.2 view/adminhtml/layout/frontbanner_banner_edit.xml

Location: app/code/FrontBanner/FrontBanner/view/adminhtml/layout/frontbanner_banner_edit.xml


<?xml version="1.0"?>
<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">
            <block class="FrontBanner\FrontBanner\Block\Adminhtml\Banner\Edit" name="frontbanner_banner_edit"/>
        </referenceContainer>
    </body>
</page>
    

11.3 view/frontend/layout/cms_index_index.xml

Location: app/code/FrontBanner/FrontBanner/view/frontend/layout/cms_index_index.xml


<?xml version="1.0"?>
<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">
            <block class="FrontBanner\FrontBanner\Block\BannerSlider" 
                   name="frontbanner.slider" 
                   template="FrontBanner_FrontBanner::slider.phtml" 
                   before="-" />
        </referenceContainer>
    </body>
</page>
    

Chapter 12: Template Files

12.1 view/frontend/templates/slider.phtml

Location: app/code/FrontBanner/FrontBanner/view/frontend/templates/slider.phtml


<?php
/**
 * @var \FrontBanner\FrontBanner\Block\BannerSlider $block
 */
$banners = $block->getBanners();
$sliderConfig = $block->getSliderConfig();
?>

<?php if ($banners->count() > 0): ?>
    <div class="frontbanner-slider" data-mage-init='{"frontbannerSlider": }'>
        <div class="banner-slider">
            <?php foreach ($banners as $banner): ?>
                <div class="banner-item">
                    <?php if ($banner->getLink()): ?>
                        <a href="<?= $block->escapeUrl($banner->getLink()) ?>" 
                           title="<?= $block->escapeHtmlAttr($banner->getTitle()) ?>">
                    </?php endif; ?>
                    
                    <img src="<?= $block->escapeUrl($block->getBannerImageUrl($banner)) ?>" 
                         alt="<?= $block->escapeHtmlAttr($banner->getTitle()) ?>" 
                         title="<?= $block->escapeHtmlAttr($banner->getTitle()) ?>"/>
                    
                    <?php if ($banner->getDescription()): ?>
                        <div class="banner-description">
                            <?= $block->escapeHtml($banner->getDescription()) ?>
                        </div>
                    <?php endif; ?>
                    
                    <?php if ($banner->getLink()): ?>
                        </a>
                    <?php endif; ?>
                </div>
            <?php endforeach; ?>
        </div>
    </div>

    <style>
        .frontbanner-slider {
            margin: 20px 0;
            position: relative;
        }
        .banner-item {
            position: relative;
            text-align: center;
        }
        .banner-item img {
            max-width: 100%;
            height: auto;
        }
        .banner-description {
            position: absolute;
            bottom: 20px;
            left: 20px;
            right: 20px;
            background: rgba(0,0,0,0.7);
            color: #fff;
            padding: 10px;
            border-radius: 5px;
        }
        .slick-prev, .slick-next {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            z-index: 10;
            background: rgba(0,0,0,0.5);
            color: #fff;
            border: none;
            padding: 10px 15px;
            cursor: pointer;
        }
        .slick-prev {
            left: 10px;
        }
        .slick-next {
            right: 10px;
        }
        .slick-dots {
            position: absolute;
            bottom: 10px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            list-style: none;
            margin: 0;
            padding: 0;
        }
        .slick-dots li {
            margin: 0 5px;
        }
        .slick-dots button {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: rgba(255,255,255,0.5);
            border: none;
            text-indent: -9999px;
            cursor: pointer;
        }
        .slick-dots li.slick-active button {
            background: #fff;
        }
    </style>

    <script>
        require(['jquery', 'slick'], function($) {
            $(document).ready(function() {
                $('.banner-slider').slick({
                    autoplay: true,
                    autoplaySpeed: 5000,
                    dots: true,
                    arrows: true,
                    infinite: true,
                    speed: 500,
                    slidesToShow: 1,
                    slidesToScroll: 1
                });
            });
        });
    </script>
<?php endif; ?>
    

Chapter 13: Test Controllers

13.1 Controller/Adminhtml/Hello/World.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Hello/World.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Hello;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;

class World extends Action
{
    /**
     * Execute action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultFactory->create(ResultFactory::TYPE_RAW);
        $result->setContents('Hello World from Admin');
        return $result;
    }
}
    

13.2 Controller/Adminhtml/Index/Index.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Index/Index.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Index;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;

class Index extends Action
{
    /**
     * Execute action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultFactory->create(ResultFactory::TYPE_RAW);
        $result->setContents('Index Controller Test');
        return $result;
    }
}
    

13.3 Controller/Adminhtml/Ping/Index.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Ping/Index.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Ping;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;

class Index extends Action
{
    /**
     * Execute action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultFactory->create(ResultFactory::TYPE_JSON);
        $result->setData(['status' => 'pong', 'timestamp' => time()]);
        return $result;
    }
}
    

13.4 Controller/Adminhtml/Test/Index.php

Location: app/code/FrontBanner/FrontBanner/Controller/Adminhtml/Test/Index.php


<?php
namespace FrontBanner\FrontBanner\Controller\Adminhtml\Test;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;

class Index extends Action
{
    /**
     * Execute action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultFactory->create(ResultFactory::TYPE_RAW);
        $result->setContents('Test Controller - Module is working!');
        return $result;
    }
}
    

Chapter 14: Module Installation Guide

Step 1: Create Module Directory Structure


mkdir -p app/code/FrontBanner/FrontBanner/{etc,Controller,Model,Block,Setup,view}
    

Step 2: Copy All Files

Copy all the files provided in this tutorial to their respective locations in the module directory.

Step 3: Run Magento Commands


php bin/magento module:enable FrontBanner_FrontBanner
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento cache:flush
    

Step 4: Verify Installation

  • Go to Admin Panel → Content → Banner Manager
  • Create a new banner with image
  • Check the frontend homepage to see the banner slider

Step 5: Troubleshooting


# Check if module is enabled
php bin/magento module:status | grep FrontBanner

# Check for errors in log
tail -f var/log/system.log
tail -f var/log/exception.log

# Re-run setup upgrade if needed
php bin/magento setup:upgrade

# Recompile if using production mode
php bin/magento deploy:mode:set developer
    

Chapter 15: Module Features Summary

Admin Features:

  • ✓ Banner grid with sortable columns
  • ✓ Add new banner functionality
  • ✓ Edit existing banners
  • ✓ Delete banners with confirmation
  • ✓ AJAX image upload with preview
  • ✓ Image preview in grid
  • ✓ Status management (Enable/Disable)
  • ✓ Sort order management
  • ✓ ACL permissions for security

Frontend Features:

  • ✓ Automatic banner slider on homepage
  • ✓ Responsive design
  • ✓ Slick slider integration
  • ✓ Banner links/URLs
  • ✓ Banner descriptions
  • ✓ Sort order respected
  • ✓ Only enabled banners shown

Technical Features:

  • ✓ Magento 2 coding standards
  • ✓ Declarative schema setup
  • ✓ Dependency injection ready
  • ✓ Event observers ready
  • ✓ Plugin/interceptor support
  • ✓ Translation ready
  • ✓ PSR-4 autoloading

Conclusion

This comprehensive Magento 2 FrontBanner module provides a complete banner management system
with both admin and frontend functionality. The module follows Magento 2 best practices and
includes all the necessary components for a production-ready extension.

The module structure demonstrates proper separation of concerns with:

  • Controllers – Handle HTTP requests and responses
  • Models – Business logic and data abstraction
  • Blocks – Presentation logic and data preparation
  • Templates – HTML markup with PHP
  • Layouts – Page structure definition
  • etc – Configuration files

This module can be extended further with additional features such as:

  • Widget support for placing banners anywhere
  • Scheduled banner display
  • Multiple banner positions
  • Banner statistics and tracking
  • Responsive image optimization
  • Multi-store support

Download GitHub Code

Happy Coding!

Leave a Reply

Your email address will not be published. Required fields are marked *