I want to discribe How I have created image upload field in an admin form.
My module's name is Paracrab\Banners
- 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.
- Will create admin Controller and Action
<?php namespace Paracrab\Banners\Controller\Adminhtml\Banner; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterface { /** * @var \Paracrab\Banners\Model\ImageUploader */ protected $imageUploader; public function __construct( \Magento\Backend\App\Action\Context $context, \Paracrab\Banners\Model\ImageUploader $imageUploader ) { parent::__construct($context); $this->imageUploader = $imageUploader; } /** * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface */ 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 \Paracrab\Banners\Model\ImageUploader
in __construct
?
You need create this class.
- Create
Paracrab\Banners\Model\ImageUploader
class
<?php namespace Paracrab\Banners\Model; class ImageUploader { /** * @var \Magento\MediaStorage\Helper\File\Storage\Database */ protected $coreFileStorageDatabase; /** * @var \Magento\Framework\Filesystem\Directory\WriteInterface */ protected $mediaDirectory; /** * @var \Magento\MediaStorage\Model\File\UploaderFactory */ private $uploaderFactory; /** * @var \Magento\Store\Model\StoreManagerInterface */ protected $storeManager; /** * @var \Psr\Log\LoggerInterface */ protected $logger; /** * @var */ protected $baseTmpPath; /** * @var */ protected $basePath; /** * @var */ protected $allowedExtensions; /** * @var array */ private $allowedMimeTypes; /** * ImageUploader constructor. * @param \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Psr\Log\LoggerInterface $logger * @param $baseTmpPath * @param $basePath * @param $allowedExtensions * @param array $allowedMimeTypes * @throws \Magento\Framework\Exception\FileSystemException */ public function __construct( \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase, \Magento\Framework\Filesystem $filesystem, \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Psr\Log\LoggerInterface $logger, $baseTmpPath, $basePath, $allowedExtensions, $allowedMimeTypes = [] ) { $this->coreFileStorageDatabase = $coreFileStorageDatabase; $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::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 \Magento\Framework\Exception\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 \Magento\Framework\Exception\LocalizedException( __('Something went wrong while saving the file(s).') ); } return $imageName; } /** * @param $fileId * @return array * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function saveFileToTmpDir($fileId) { $baseTmpPath = $this->getBaseTmpPath(); /** @var \Magento\MediaStorage\Model\File\Uploader $uploader */ $uploader = $this->uploaderFactory->create(['fileId' => $fileId]); $uploader->setAllowedExtensions($this->getAllowedExtensions()); $uploader->setAllowRenameFiles(true); if (!$uploader->checkMimeType($this->allowedMimeTypes)) { throw new \Magento\Framework\Exception\LocalizedException(__('File validation failed.')); } $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath)); unset($result['path']); if (!$result) { throw new \Magento\Framework\Exception\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['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 \Magento\Framework\Exception\LocalizedException( __('Something went wrong while saving the file(s).') ); } } return $result; } }
This class was copied from
vendor/magento/module-catalog/Model/ImageUploader.php
- You need create
virtualType
for this class inetc/di.xml
with parameters
<virtualType name="Paracrab\Banners\ImageUploader" type="Paracrab\Banners\Model\ImageUploader"> <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="Paracrab\Banners\Controller\Adminhtml\Banner\Upload"> <arguments> <argument name="imageUploader" xsi:type="object">Paracrab\Banners\ImageUploader</argument> </arguments> </type> <type name="Paracrab\Banners\Controller\Adminhtml\Banners\Save"> <arguments> <argument name="imageUploader" xsi:type="object">Paracrab\Banners\ImageUploader</argument> </arguments> </type>
- Also You should create class
Paracrab/Banners/Model/Banner/FileInfo.php
<?php namespace Paracrab\Banners\Model\Banner; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\File\Mime; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\Filesystem\Directory\ReadInterface; /** * 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 \Magento\Store\Model\StoreManagerInterface */ protected $_storeManager; /** * @param Filesystem $filesystem * @param Mime $mime */ public function __construct( Filesystem $filesystem, Mime $mime, \Magento\Store\Model\StoreManagerInterface $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 \Magento\Framework\Exception\NoSuchEntityException */ public function getAbsolutePatch($fileName) { $absoluteFilePath = $this->_storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::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
- You have
DataProvider
for displaing form in whole. This is code myDataProvider
class
<?php namespace Paracrab\Banners\Model\Banner; use Paracrab\Banners\Model\ResourceModel\Banner\CollectionFactory as BannerCollectionFactory; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Ui\DataProvider\Modifier\PoolInterface; use Paracrab\Banners\Api\BannerRepositoryInterface; use Paracrab\Banners\Model\Banner\FileInfo; class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider { /** * @var \Paracrab\Banners\Model\ResourceModel\Banner\Collection */ protected $collection; /** * @var DataPersistorInterface */ protected $dataPersistor; /** * @var */ protected $loadedData; /** * @var \Magento\Framework\App\Request\Http */ protected $request; /** * @var BannerRepositoryInterface */ protected $bannerRepository; /** * @var \Paracrab\Banners\Model\Banner\FileInfo */ protected $fileInfo; /** * DataProvider constructor. * @param $name * @param $primaryFieldName * @param $requestFieldName * @param BannerCollectionFactory $pageCollectionFactory * @param DataPersistorInterface $dataPersistor * @param \Magento\Framework\App\Request\Http $request * @param BannerRepositoryInterface $bannerRepository * @param \Paracrab\Banners\Model\Banner\FileInfo $fileInfo * @param array $meta * @param array $data * @param PoolInterface|null $pool */ public function __construct( $name, $primaryFieldName, $requestFieldName, BannerCollectionFactory $pageCollectionFactory, DataPersistorInterface $dataPersistor, \Magento\Framework\App\Request\Http $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 \Magento\Framework\Exception\LocalizedException */ 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 Paracrab\Banners\Controller\Adminhtml\Banners; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action\Context; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Exception\LocalizedException; use Paracrab\Banners\Model\Banner; use Paracrab\Banners\Model\BannerFactory; use Paracrab\Banners\Api\BannerRepositoryInterface; use Paracrab\Banners\Model\ImageUploader; class Save extends \Magento\Backend\App\Action 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 \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\Result\Redirect|\Magento\Framework\Controller\ResultInterface */ 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; } }
- Create directories in
pub/media
likeparacrab/images
andparacrab/tmp/images