Skip to content

Magento 2.3. How to create image upload field in an admin form

I want to discribe How I have created image upload field in an admin form.

My module’s name is ParacrabBanners

  1. Firstly You should add field in Paracrab/Banners/view/adminhtml/ui_component/name_name_form.xml

<field name="image" sortOrder="30" formElement="imageUploader">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">banner</item>
</item>
</argument>
<settings>
<elementTmpl>ui/form/element/uploader/image</elementTmpl>
<dataType>string</dataType>
<label translate="true">Banner Image</label>
<visible>true</visible>
<required>false</required>
</settings>
<formElements>
<imageUploader>
<settings>
<required>false</required>
<uploaderConfig>
<param xsi:type="url" name="url" path="banner/banner/upload"/>
</uploaderConfig>
<openDialogTitle>Media Gallery</openDialogTitle>
<initialMediaGalleryOpenSubpath>paracrab/image</initialMediaGalleryOpenSubpath>
<allowedExtensions>jpg jpeg gif png</allowedExtensions>
<maxFileSize>4194304</maxFileSize>
</settings>
</imageUploader>
</formElements>
</field>

name="image"it is name field in database.

  1. Will create admin Controller and Action
<?php
namespace ParacrabBannersControllerAdminhtmlBanner;

use MagentoFrameworkAppActionHttpPostActionInterface;
use MagentoFrameworkControllerResultFactory;

class Upload extends MagentoBackendAppAction implements HttpPostActionInterface
{

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


    public function __construct(
        MagentoBackendAppActionContext $context,
        ParacrabBannersModelImageUploader $imageUploader
    ) {
        parent::__construct($context);
        $this->imageUploader = $imageUploader;
    }

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

        try {
            $result = $this->imageUploader->saveFileToTmpDir($imageId);
        } catch (Exception $e) {
            $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
        }
        return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
    }
}

Did you notice ParacrabBannersModelImageUploader in __construct ?
You need create this class.

  1. Create ParacrabBannersModelImageUploader class
<?php
namespace ParacrabBannersModel;


class ImageUploader
{

    /**
     * @var MagentoMediaStorageHelperFileStorageDatabase
     */
    protected $coreFileStorageDatabase;

    /**
     * @var MagentoFrameworkFilesystemDirectoryWriteInterface
     */
    protected $mediaDirectory;

    /**
     * @var MagentoMediaStorageModelFileUploaderFactory
     */
    private $uploaderFactory;

    /**
     * @var MagentoStoreModelStoreManagerInterface
     */
    protected $storeManager;

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

    /**
     * @var
     */
    protected $baseTmpPath;

    /**
     * @var
     */
    protected $basePath;

    /**
     * @var
     */
    protected $allowedExtensions;

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

    /**
     * ImageUploader constructor.
     * @param MagentoMediaStorageHelperFileStorageDatabase $coreFileStorageDatabase
     * @param MagentoFrameworkFilesystem $filesystem
     * @param MagentoMediaStorageModelFileUploaderFactory $uploaderFactory
     * @param MagentoStoreModelStoreManagerInterface $storeManager
     * @param PsrLogLoggerInterface $logger
     * @param $baseTmpPath
     * @param $basePath
     * @param $allowedExtensions
     * @param array $allowedMimeTypes
     * @throws MagentoFrameworkExceptionFileSystemException
     */
    public function __construct(
        MagentoMediaStorageHelperFileStorageDatabase $coreFileStorageDatabase,
        MagentoFrameworkFilesystem $filesystem,
        MagentoMediaStorageModelFileUploaderFactory $uploaderFactory,
        MagentoStoreModelStoreManagerInterface $storeManager,
        PsrLogLoggerInterface $logger,
        $baseTmpPath,
        $basePath,
        $allowedExtensions,
        $allowedMimeTypes = []
    ) {
        $this->coreFileStorageDatabase = $coreFileStorageDatabase;
        $this->mediaDirectory = $filesystem->getDirectoryWrite(MagentoFrameworkAppFilesystemDirectoryList::MEDIA);
        $this->uploaderFactory = $uploaderFactory;
        $this->storeManager = $storeManager;
        $this->logger = $logger;
        $this->baseTmpPath = $baseTmpPath;
        $this->basePath = $basePath;
        $this->allowedExtensions = $allowedExtensions;
        $this->allowedMimeTypes = $allowedMimeTypes;
    }


    /**
     * @param $baseTmpPath
     */
    public function setBaseTmpPath($baseTmpPath)
    {
        $this->baseTmpPath = $baseTmpPath;
    }


    /**
     * @param $basePath
     */
    public function setBasePath($basePath)
    {
        $this->basePath = $basePath;
    }

    /**
     * @param $allowedExtensions
     */
    public function setAllowedExtensions($allowedExtensions)
    {
        $this->allowedExtensions = $allowedExtensions;
    }

    /**
     * @return mixed
     */
    public function getBaseTmpPath()
    {
        return $this->baseTmpPath;
    }

    /**
     * @return mixed
     */
    public function getBasePath()
    {
        return $this->basePath;
    }

    /**
     * @return mixed
     */
    public function getAllowedExtensions()
    {
        return $this->allowedExtensions;
    }

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

    /**
     * @param $imageName
     * @return mixed
     * @throws MagentoFrameworkExceptionLocalizedException
     */
    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 MagentoFrameworkExceptionLocalizedException(
                __('Something went wrong while saving the file(s).')
            );
        }

        return $imageName;
    }

    /**
     * @param $fileId
     * @return array
     * @throws MagentoFrameworkExceptionLocalizedException
     * @throws MagentoFrameworkExceptionNoSuchEntityException
     */
    public function saveFileToTmpDir($fileId)
    {
        $baseTmpPath = $this->getBaseTmpPath();

        /** @var MagentoMediaStorageModelFileUploader $uploader */
        $uploader = $this->uploaderFactory->create(['fileId' => $fileId]);
        $uploader->setAllowedExtensions($this->getAllowedExtensions());
        $uploader->setAllowRenameFiles(true);
        if (!$uploader->checkMimeType($this->allowedMimeTypes)) {
            throw new MagentoFrameworkExceptionLocalizedException(__('File validation failed.'));
        }
        $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath));

        unset($result['path']);

        if (!$result) {
            throw new MagentoFrameworkExceptionLocalizedException(
                __('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['url'] = $this->storeManager
                ->getStore()
                ->getBaseUrl(
                    MagentoFrameworkUrlInterface::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 MagentoFrameworkExceptionLocalizedException(
                    __('Something went wrong while saving the file(s).')
                );
            }
        }

        return $result;
    }
}

This class was copied from

vendor/magento/module-catalog/Model/ImageUploader.php
  1. You need create virtualType for this class in etc/di.xml with parameters
 <virtualType name="ParacrabBannersImageUploader" type="ParacrabBannersModelImageUploader">
        <arguments>
            <argument name="baseTmpPath" xsi:type="string">paracrab/tmp/image</argument>
            <argument name="basePath" xsi:type="string">paracrab/image</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="gif" xsi:type="string">gif</item>
                <item name="png" xsi:type="string">png</item>
            </argument>
            <argument name="allowedMimeTypes" xsi:type="array">
                <item name="jpg" xsi:type="string">image/jpg</item>
                <item name="jpeg" xsi:type="string">image/jpeg</item>
                <item name="gif" xsi:type="string">image/gif</item>
                <item name="png" xsi:type="string">image/png</item>
            </argument>
        </arguments>
    </virtualType>
    <type name="ParacrabBannersControllerAdminhtmlBannerUpload">
        <arguments>
            <argument name="imageUploader" xsi:type="object">ParacrabBannersImageUploader</argument>
        </arguments>
    </type>
    <type name="ParacrabBannersControllerAdminhtmlBannersSave">
        <arguments>
            <argument name="imageUploader" xsi:type="object">ParacrabBannersImageUploader</argument>
        </arguments>
    </type>
  1. Also You should create class Paracrab/Banners/Model/Banner/FileInfo.php
<?php
namespace ParacrabBannersModelBanner;

use MagentoFrameworkAppFilesystemDirectoryList;
use MagentoFrameworkFileMime;
use MagentoFrameworkFilesystem;
use MagentoFrameworkFilesystemDirectoryWriteInterface;
use MagentoFrameworkFilesystemDirectoryReadInterface;

/**
 * Class FileInfo
 *
 * Provides information about requested file
 */
class FileInfo
{
    /**
     * Path in /pub/media directory
     */
    const ENTITY_MEDIA_PATH = 'paracrab/image';

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

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

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

    /**
     * @var ReadInterface
     */
    private $baseDirectory;


    /**
     * @var MagentoStoreModelStoreManagerInterface
     */
    protected $_storeManager;

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

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

    /**
     * Get Base Directory read instance
     *
     * @return ReadInterface
     */
    private function getBaseDirectory()
    {
        if (!isset($this->baseDirectory)) {
            $this->baseDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT);
        }

        return $this->baseDirectory;
    }

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

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

    /**
     * @param $fileName
     * @return string
     * @throws MagentoFrameworkExceptionNoSuchEntityException
     */
    public function getAbsolutePatch($fileName)
    {

        $absoluteFilePath = $this->_storeManager->getStore()->getBaseUrl(MagentoFrameworkUrlInterface::URL_TYPE_MEDIA);
        $absoluteFilePath .= $this->getFilePath($fileName);
        return $absoluteFilePath;
    }

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

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

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

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

    /**
     * Construct and return file subpath based on filename relative to media directory
     *
     * @param string $fileName
     * @return string
     */
    private function getFilePath($fileName)
    {
        $filePath = ltrim($fileName, '/');

        $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath();
        $isFileNameBeginsWithMediaDirectoryPath = $this->isBeginsWithMediaDirectoryPath($fileName);

        // if the file is not using a relative path, it resides in the paracrab/image media directory
        $fileIsInModuleMediaDir = !$isFileNameBeginsWithMediaDirectoryPath;

        if ($fileIsInModuleMediaDir) {
            $filePath = self::ENTITY_MEDIA_PATH . '/' . $filePath;
        } else {
            $filePath = substr($filePath, strlen($mediaDirectoryRelativeSubpath));
        }

        return $filePath;
    }

    /**
     * Checks for whether $fileName string begins with media directory path
     *
     * @param string $fileName
     * @return bool
     */
    public function isBeginsWithMediaDirectoryPath($fileName)
    {
        $filePath = ltrim($fileName, '/');

        $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath();
        $isFileNameBeginsWithMediaDirectoryPath = strpos($filePath, $mediaDirectoryRelativeSubpath) === 0;

        return $isFileNameBeginsWithMediaDirectoryPath;
    }

    /**
     * Get media directory subpath relative to base directory path
     *
     * @return string
     */
    private function getMediaDirectoryPathRelativeToBaseDirectoryPath()
    {
        $baseDirectoryPath = $this->getBaseDirectory()->getAbsolutePath();
        $mediaDirectoryPath = $this->getMediaDirectory()->getAbsolutePath();

        $mediaDirectoryRelativeSubpath = substr($mediaDirectoryPath, strlen($baseDirectoryPath));

        return $mediaDirectoryRelativeSubpath;
    }
}

This class similar this vendor/magento/module-catalog/Model/Category/FileInfo.php

  1. You have DataProvider for displaing form in whole. This is code my DataProvider class
<?php
namespace ParacrabBannersModelBanner;

use ParacrabBannersModelResourceModelBannerCollectionFactory as BannerCollectionFactory;
use MagentoFrameworkAppRequestDataPersistorInterface;
use MagentoUiDataProviderModifierPoolInterface;
use ParacrabBannersApiBannerRepositoryInterface;
use ParacrabBannersModelBannerFileInfo;

class DataProvider extends MagentoUiDataProviderModifierPoolDataProvider
{
    /**
     * @var ParacrabBannersModelResourceModelBannerCollection
     */
    protected $collection;

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

    /**
     * @var
     */
    protected $loadedData;

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

    /**
     * @var BannerRepositoryInterface
     */
    protected $bannerRepository;

    /**
     * @var ParacrabBannersModelBannerFileInfo
     */
    protected $fileInfo;

    /**
     * DataProvider constructor.
     * @param $name
     * @param $primaryFieldName
     * @param $requestFieldName
     * @param BannerCollectionFactory $pageCollectionFactory
     * @param DataPersistorInterface $dataPersistor
     * @param MagentoFrameworkAppRequestHttp $request
     * @param BannerRepositoryInterface $bannerRepository
     * @param ParacrabBannersModelBannerFileInfo $fileInfo
     * @param array $meta
     * @param array $data
     * @param PoolInterface|null $pool
     */
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        BannerCollectionFactory $pageCollectionFactory,
        DataPersistorInterface $dataPersistor,
        MagentoFrameworkAppRequestHttp $request,
        BannerRepositoryInterface $bannerRepository,
        FileInfo $fileInfo,
        array $meta = [],
        array $data = [],
        PoolInterface $pool = null
    ) {
        $this->collection = $pageCollectionFactory->create();
        $this->dataPersistor = $dataPersistor;
        $this->request = $request;
        $this->bannerRepository = $bannerRepository;
        $this->fileInfo = $fileInfo;
        parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool);
    }

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

        $bannerId = $this->request->getParam('banner_id');

        if($bannerId) {
            $item = $this->bannerRepository->getById($bannerId);
            $fileName = $item->getData('image');

            if($this->fileInfo->isExist($fileName) && $fileName !== '') {
                $stat = $this->fileInfo->getStat($fileName);
                $image = [
                    0 => [
                      'name' =>  basename($fileName),
                      'url' => $this->fileInfo->getAbsolutePatch($fileName),
                      'size' => isset($stat) ? $stat['size'] : 0,
                      'type' => $this->fileInfo->getMimeType($fileName)
                    ]
                ];

                $item->setData('image', $image);

            }

            $this->loadedData[$item->getId()] = $item->getData();
        }

        $data = $this->dataPersistor->get('paracrab_banner');

        if (!empty($data)) {
            $page = $this->collection->getNewEmptyItem();
            $page->setData($data);
            $this->loadedData[$page->getId()] = $page->getData();
            $this->dataPersistor->clear('paracrab_banner');
        }

        return $this->loadedData;
    }
}

7. You have Action for saving data from form. This is how my Action look like

<?php
namespace ParacrabBannersControllerAdminhtmlBanners;

use MagentoFrameworkAppActionHttpPostActionInterface;
use MagentoBackendAppActionContext;
use MagentoFrameworkAppRequestDataPersistorInterface;
use MagentoFrameworkExceptionLocalizedException;
use ParacrabBannersModelBanner;
use ParacrabBannersModelBannerFactory;
use ParacrabBannersApiBannerRepositoryInterface;
use ParacrabBannersModelImageUploader;

class Save extends MagentoBackendAppAction implements HttpPostActionInterface
{
    /**
     * @var DataPersistorInterface
     */
    protected $dataPersistor;

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

    /**
     * @var BannerRepositoryInterface
     */
    private $bannerRepository;

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


    /**
     * Save constructor.
     * @param Context $context
     * @param DataPersistorInterface $dataPersistor
     * @param BannerFactory $bannerFactory
     * @param BannerRepositoryInterface $bannerRepository
     * @param ImageUploader $imageUploader
     */
    public function __construct
    (
        Context $context,
        DataPersistorInterface $dataPersistor,
        BannerFactory $bannerFactory,
        BannerRepositoryInterface $bannerRepository,
        ImageUploader $imageUploader
    )
    {
        $this->dataPersistor = $dataPersistor;
        $this->bannerFactory = $bannerFactory;
        $this->bannerRepository = $bannerRepository;
        $this->imageUploader = $imageUploader;
         parent::__construct($context);
    }

    /**
     * @return MagentoFrameworkAppResponseInterface|MagentoFrameworkControllerResultRedirect|MagentoFrameworkControllerResultInterface
     */
    public function execute()
    {
        $resultRedirect = $this->resultRedirectFactory->create();
        $data = $this->getRequest()->getPostValue();

        if ($data) {
            if (isset($data['status']) && $data['status'] === 'true') {
                $data['status'] = Banner::STATUS_ENABLED;
            }
            if (empty($data['banner_id'])) {
                $data['banner_id'] = null;
            }

            if(isset($data['image'])) {
                $fileName = $data['image'][0]['name'];
                $data['image'] = $fileName;
                $this->imageUploader->moveFileFromTmp($fileName);
            } else {
                $data['image'] = '';
            }

            $model = $this->bannerFactory->create();

            $id = $this->getRequest()->getParam('banner_id');
            if ($id) {
                try {
                    $model = $this->bannerRepository->getById($id);
                } catch (LocalizedException $e) {
                    $this->messageManager->addErrorMessage(__('This banner no longer exists.'));
                    return $resultRedirect->setPath('*/*/');
                }
            }

            $model->setData($data);

            try {
                $this->bannerRepository->save($model);
                $this->messageManager->addSuccessMessage(__('You saved the banner.'));
                $this->dataPersistor->clear('paracrab_banner');
                return $this->processBlockReturn($model, $data, $resultRedirect);
            } catch (LocalizedException $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
            } catch (Exception $e) {
                $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the banner.'));
            }

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

    /**
     * @param $model
     * @param $data
     * @param $resultRedirect
     * @return mixed
     */
    private function processBlockReturn($model, $data, $resultRedirect)
    {
        $redirect = $data['back'] ?? 'close';

        if ($redirect ==='continue') {
            $resultRedirect->setPath('*/*/edit', ['banner_id' => $model->getId()]);
        } else if ($redirect === 'close') {
            $resultRedirect->setPath('*/*/');
        }
        return $resultRedirect;
    }

}
  1. Create directories in pub/media like paracrab/images and paracrab/tmp/images