Skip to main content
added necessary declaration of FrameworkException
Source Link
<?php
namespace [Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper;
use Magento\Framework\Data\Form\Element\Image as ImageField;
use Magento\Framework\Data\Form\Element\Factory as ElementFactory;
use Magento\Framework\Data\Form\Element\CollectionFactory as ElementCollectionFactory;
use Magento\Framework\Escaper;
use [Namespace]\[Module]\Model\[Entity]\Image as [Entity]Image;
use Magento\Framework\UrlInterface; 

/**
 * @method string getValue()
 */
class Image extends ImageField
{
    /**
     * image model
     *
     * @var \[Namespace]\[Module]\Model\[Entity]\Image
     */
    protected $imageModel; 

    /**
     * @param [Entity]Image $imageModel
     * @param ElementFactory $factoryElement
     * @param ElementCollectionFactory $factoryCollection
     * @param Escaper $escaper
     * @param UrlInterface $urlBuilder
     * @param array $data
     */
    public function __construct(
        [Entity]Image $imageModel,
        ElementFactory $factoryElement,
        ElementCollectionFactory $factoryCollection,
        Escaper $escaper,
        UrlInterface $urlBuilder,
        $data = []
    )
    {
        $this->imageModel = $imageModel;
        parent::__construct($factoryElement, $factoryCollection, $escaper, $urlBuilder, $data);
    }
    /**
     * Get image preview url
     *
     * @return string
     */
    protected function _getUrl()
    {
        $url = false;
        if ($this->getValue()) {
            $url = $this->imageModel->getBaseUrl().$this->getValue();
        }
        return $url;
    }
}
<?php
use Magento\Framework\Exception\LocalizedException as FrameworkException;

class ....

protected $uploaderFactory;
protected $imageModel;

public function __construct(
    ....
    \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
    \[Namespace]\[Module]\Model\[Entity]\Image $imageModel,
    ....
){
    ...
    $this->uploaderFactory = $uploaderFactory;
    $this->imageModel = $imageModel;
    ...
}
<?php
namespace [Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper;
use Magento\Framework\Data\Form\Element\Image as ImageField;
use Magento\Framework\Data\Form\Element\Factory as ElementFactory;
use Magento\Framework\Data\Form\Element\CollectionFactory as ElementCollectionFactory;
use Magento\Framework\Escaper;
use [Namespace]\[Module]\Model\[Entity]\Image as [Entity]Image;
use Magento\Framework\UrlInterface;
/**
 * @method string getValue()
 */
class Image extends ImageField
{
    /**
     * image model
     *
     * @var \[Namespace]\[Module]\Model\[Entity]\Image
     */
    protected $imageModel;
    /**
     * @param [Entity]Image $imageModel
     * @param ElementFactory $factoryElement
     * @param ElementCollectionFactory $factoryCollection
     * @param Escaper $escaper
     * @param UrlInterface $urlBuilder
     * @param array $data
     */
    public function __construct(
        [Entity]Image $imageModel,
        ElementFactory $factoryElement,
        ElementCollectionFactory $factoryCollection,
        Escaper $escaper,
        UrlInterface $urlBuilder,
        $data = []
    )
    {
        $this->imageModel = $imageModel;
        parent::__construct($factoryElement, $factoryCollection, $escaper, $urlBuilder, $data);
    }
    /**
     * Get image preview url
     *
     * @return string
     */
    protected function _getUrl()
    {
        $url = false;
        if ($this->getValue()) {
            $url = $this->imageModel->getBaseUrl().$this->getValue();
        }
        return $url;
    }
}
<?php
class ....

protected $uploaderFactory;
protected $imageModel;

public function __construct(
    ....
    \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
    \[Namespace]\[Module]\Model\[Entity]\Image $imageModel,
    ....
){
    ...
    $this->uploaderFactory = $uploaderFactory;
    $this->imageModel = $imageModel;
    ...
}
<?php
namespace [Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper;
use Magento\Framework\Data\Form\Element\Image as ImageField;
use Magento\Framework\Data\Form\Element\Factory as ElementFactory;
use Magento\Framework\Data\Form\Element\CollectionFactory as ElementCollectionFactory;
use Magento\Framework\Escaper;
use [Namespace]\[Module]\Model\[Entity]\Image as [Entity]Image;
use Magento\Framework\UrlInterface; 

/**
 * @method string getValue()
 */
class Image extends ImageField
{
    /**
     * image model
     *
     * @var \[Namespace]\[Module]\Model\[Entity]\Image
     */
    protected $imageModel; 

    /**
     * @param [Entity]Image $imageModel
     * @param ElementFactory $factoryElement
     * @param ElementCollectionFactory $factoryCollection
     * @param Escaper $escaper
     * @param UrlInterface $urlBuilder
     * @param array $data
     */
    public function __construct(
        [Entity]Image $imageModel,
        ElementFactory $factoryElement,
        ElementCollectionFactory $factoryCollection,
        Escaper $escaper,
        UrlInterface $urlBuilder,
        $data = []
    )
    {
        $this->imageModel = $imageModel;
        parent::__construct($factoryElement, $factoryCollection, $escaper, $urlBuilder, $data);
    }
    /**
     * Get image preview url
     *
     * @return string
     */
    protected function _getUrl()
    {
        $url = false;
        if ($this->getValue()) {
            $url = $this->imageModel->getBaseUrl().$this->getValue();
        }
        return $url;
    }
}
<?php
use Magento\Framework\Exception\LocalizedException as FrameworkException;

class ....

protected $uploaderFactory;
protected $imageModel;

public function __construct(
    ....
    \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
    \[Namespace]\[Module]\Model\[Entity]\Image $imageModel,
    ....
){
    ...
    $this->uploaderFactory = $uploaderFactory;
    $this->imageModel = $imageModel;
    ...
}
added 6 characters in body
Source Link
Marius
  • 198.5k
  • 55
  • 424
  • 833
$fieldset->addType('image', '\[Namespace]\[Module]\Adminhtml\[Entity]\Helper\Image''\[Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper\Image');
$fieldset->addType('image', '\[Namespace]\[Module]\Adminhtml\[Entity]\Helper\Image');
$fieldset->addType('image', '\[Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper\Image');
Source Link
Marius
  • 198.5k
  • 55
  • 424
  • 833

You need to create a class to handle your image upload field and show the image once is uploaded.

So create [Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper\Image class

<?php
namespace [Namespace]\[Module]\Block\Adminhtml\[Entity]\Helper;
use Magento\Framework\Data\Form\Element\Image as ImageField;
use Magento\Framework\Data\Form\Element\Factory as ElementFactory;
use Magento\Framework\Data\Form\Element\CollectionFactory as ElementCollectionFactory;
use Magento\Framework\Escaper;
use [Namespace]\[Module]\Model\[Entity]\Image as [Entity]Image;
use Magento\Framework\UrlInterface;
/**
 * @method string getValue()
 */
class Image extends ImageField
{
    /**
     * image model
     *
     * @var \[Namespace]\[Module]\Model\[Entity]\Image
     */
    protected $imageModel;
    /**
     * @param [Entity]Image $imageModel
     * @param ElementFactory $factoryElement
     * @param ElementCollectionFactory $factoryCollection
     * @param Escaper $escaper
     * @param UrlInterface $urlBuilder
     * @param array $data
     */
    public function __construct(
        [Entity]Image $imageModel,
        ElementFactory $factoryElement,
        ElementCollectionFactory $factoryCollection,
        Escaper $escaper,
        UrlInterface $urlBuilder,
        $data = []
    )
    {
        $this->imageModel = $imageModel;
        parent::__construct($factoryElement, $factoryCollection, $escaper, $urlBuilder, $data);
    }
    /**
     * Get image preview url
     *
     * @return string
     */
    protected function _getUrl()
    {
        $url = false;
        if ($this->getValue()) {
            $url = $this->imageModel->getBaseUrl().$this->getValue();
        }
        return $url;
    }
}

then create the class that will help you retrieve the image upload path and upload dir

<?php
namespace [Namespace]\[Module]\Model\[Entity];
use Magento\Framework\UrlInterface;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;
class Image
{
    /**
     * media sub folder
     * @var string
     */
    protected $subDir = '[namespace]/[module]/[entity]';
    /**
     * url builder
     *
     * @var \Magento\Framework\UrlInterface
     */
    protected $urlBuilder;
    /**
     * @var \Magento\Framework\Filesystem
     */
    protected $fileSystem;
    /**
     * @param UrlInterface $urlBuilder
     * @param Filesystem $fileSystem
     */
    public function __construct(
        UrlInterface $urlBuilder,
        Filesystem $fileSystem
    )
    {
        $this->urlBuilder = $urlBuilder;
        $this->fileSystem = $fileSystem;
    }
    /**
     * get images base url
     *
     * @return string
     */
    public function getBaseUrl()
    {
        return $this->urlBuilder->getBaseUrl(['_type' => UrlInterface::URL_TYPE_MEDIA]).$this->subDir.'/image';
    }
    /**
     * get base image dir
     *
     * @return string
     */
    public function getBaseDir()
    {
        return $this->fileSystem->getDirectoryWrite(DirectoryList::MEDIA)->getAbsolutePath($this->subDir.'/image');
    }
}

now in your edit for tab add this in the method _prepareForm right after declaring the fieldset

$fieldset->addType('image', '\[Namespace]\[Module]\Adminhtml\[Entity]\Helper\Image');

and add your image field like this

$fieldset->addField(
    'image_field_name',
    'image',
    [
        'name'        => 'image_field_name',
        'label'       => __('Image field Label'),
        'title'       => __('Image field Label'),
    ]
);

In the controller that saves your entity you need to inject in the constructor the following classes

Magento\MediaStorage\Model\File\UploaderFactory and [Namespace]\[Module]\Model\[Entity]\Image

So make your class look like this

<?php
class ....

protected $uploaderFactory;
protected $imageModel;

public function __construct(
    ....
    \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
    \[Namespace]\[Module]\Model\[Entity]\Image $imageModel,
    ....
){
    ...
    $this->uploaderFactory = $uploaderFactory;
    $this->imageModel = $imageModel;
    ...
}

Now, in the same controller add this, before calling $[entity]->save()

$imageName = $this->uploadFileAndGetName('image_field_name', $this->imageModel->getBaseDir(), $data);
$[entity]->setImageFieldName($imageName);

and create this method:

public function uploadFileAndGetName($input, $destinationFolder, $data)
{
    try {
        if (isset($data[$input]['delete'])) {
            return '';
        } else {
            $uploader = $this->uploaderFactory->create(['fileId' => $input]);
            $uploader->setAllowRenameFiles(true);
            $uploader->setFilesDispersion(true);
            $uploader->setAllowCreateFolders(true);
            $result = $uploader->save($destinationFolder);
            return $result['file'];
        }
    } catch (\Exception $e) {
        if ($e->getCode() != \Magento\Framework\File\Uploader::TMP_NAME_EMPTY) {
            throw new FrameworkException($e->getMessage());
        } else {
            if (isset($data[$input]['value'])) {
                return $data[$input]['value'];
            }
        }
    }
    return '';
}

A full example on how to create an entity that supports image and file upload (it's a bit different than described here as it contains an extra class for upload) can be found here