8

I have magento2 admin custom module with image upload functionality. I want to upload image from admin. what code should apply for display image field in from, upload image, also image display in edit action.

Thanks

File Path : app\code\[Vendor]\[Module]\Block\Adminhtml\Emp\Edit\Tab\Main.php

    /**
     * Prepare form
     *
     * @return $this
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
    */
    protected function _prepareForm()
    {
        $model = $this->_coreRegistry->registry('emp_post');

        $isElementDisabled = false;

        /** @var \Magento\Framework\Data\Form $form */
        $form = $this->_formFactory->create();

        $form->setHtmlIdPrefix('page_');

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

        if ($model->getId()) {
            $fieldset->addField('customer_id', 'hidden', ['name' => 'customer_id']);
        }

        $fieldset->addField(
            'firstname',
            'text',
            [
                'name' => 'firstname',
                'label' => __('First Name'),
                'title' => __('First Name'),
                'required' => true,
                'disabled' => $isElementDisabled,
                'value' =>'abc'
            ]
        );

        $fieldset->addField(
            'lastname',
            'text',
            [
                'name' => 'lastname',
                'label' => __('Last Name'),
                'title' => __('Last Name'),
                'required' => true,
                'disabled' => $isElementDisabled,
                'value' =>'abc'
            ]
        );

        $fieldset->addField(
            'email',
            'text',
            [
                'name' => 'email',
                'label' => __('Email Address'),
                'title' => __('Email Address'),
                'required' => true,
                'disabled' => $isElementDisabled,
                'value' =>'abc'
            ]
        );


        $fieldset->addField(
            'image',
            'image',
            array(
                'name' => 'image',
                'label' => __('Image'),
                'title' => __('Image')
            )
        );

        $fieldset->addField(
            'telephone',
            'text',
            [
                'name' => 'telephone',
                'label' => __('Telephone'),
                'title' => __('Telephone'),
                'required' => true,
                'disabled' => $isElementDisabled,
                'value' =>'abc'
            ]
        );

        $dateFormat = $this->_localeDate->getDateFormat(
            \IntlDateFormatter::SHORT
        );

        $fieldset->addField(
            'dob',
            'date',
            [
                'name' => 'dob',
                'label' => __('Date of birth'),
                'date_format' => $dateFormat,
                'disabled' => $isElementDisabled,
                'class' => 'validate-date validate-date-range date-range-custom_theme-from'
            ]
        );

        $fieldset->addField(
            'is_active',
            'select',
            [
                'label' => __('Status'),
                'title' => __('Status'),
                'name' => 'is_active',
                'required' => true,
                'options' => $this->_status->getOptionArray(),
                'disabled' => $isElementDisabled
            ]
        );
        if (!$model->getId()) {
            $model->setData('is_active', $isElementDisabled ? '0' : '1');
        }


        if($model->getData('image')){
            $model->setData('image','learning/images'.$model->getData('image'));
        }

        $form->setValues($model->getData());
        $this->setForm($form);

        return parent::_prepareForm();
    } 
benmarks
  • 16,675
  • 4
  • 41
  • 108
Suresh Chikani
  • 15,836
  • 11
  • 62
  • 99
  • The module developed https://darshanbhavsar.wordpress.com/2015/02/11/magento-2-custom-module/ looks to have everything you are looking for. – Imran Zahoor Jun 05 '16 at 19:55

4 Answers4

18

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]\Block\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
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;
    ...
}

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

Simon
  • 117
  • 6
Marius
  • 197,939
  • 53
  • 422
  • 830
  • given url exension not working. it throw error on screen exception 'Magento\Framework\Exception\LocalizedException' with message 'Source class "\Magento\Framework\View\Element\UiComponent\DataProvider\Collection" for "Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory" generation does not exist.' – Suresh Chikani Oct 13 '15 at 08:48
  • @SHPatel. Clear the contents of var/generation. If this problem persists it can for 2 reasons. You either don't have the latest version of Magento 2 or my extension does not work on the latest version. If the problem is my extension I will check it when I have some time. – Marius Oct 13 '15 at 11:11
  • @Marius, How can we display such image into adminhtml Grid section too? Right now is Default magento 2 is showing it for Product Grid. – Praful Rajput Oct 13 '15 at 13:29
  • @PrafulRajput. I didn't get to check that part out yet. I have no idea how it's done. – Marius Oct 13 '15 at 13:38
  • @marius this works in RC 2. One thing you mention adding $fieldset->addType('image', '[Namespace][Module]\Adminhtml[Entity]\Helper\Image'); I am pretty sure you are missing a "Block" callout. [Namespace][Module]\Block\Adminhtml[Entity]\Helper\Image – Eirik Nov 09 '15 at 20:47
  • @Eirik. Thanks for spotting this. I fixed it. – Marius Nov 09 '15 at 21:14
  • I think throw new FrameworkException should be throw new \PHPUnit_Framework_Exception (the former is undefined relative to the current namespace). Also if you're implementing this, don't forget to update all image_field_names with your own, including the camelcase setImageFieldName() in the controller (I lost some time to that one) – Adelmar Apr 16 '16 at 00:35
  • i'm getting this error while saving form Call to a member function isLoggedIn() on null any idea? @Marius – chirag dodia Nov 04 '16 at 06:10
  • I have no idea. – Marius Nov 04 '16 at 06:49
  • @Marius I have used above code. It working fine for all allowed file but throws exception if disallowed file-type is tried to upload. Can you help me to manage exception. – Gaurav Agrawal Jun 27 '17 at 12:22
  • @GauravAgrawal wrap the call to $imageName = $this->uploadFileAndGetName ... in a try-catch and on catch do something with the exception message. Set it as a flash message or something – Marius Jun 27 '17 at 12:39
  • @Marius I am trying to implement a similar situation. I have my Save class which extends \Magento\Backend\App\Action When I update my __constructor to include (Action\Context $context, UploaderFactory $uploaderFactory, SliderImage $imageModel) I get the error Save::__construct() must be an instance of .../UploaderFactory, none given I have debuged a bit and it seems like only the Context is ever being injected. Any idea? – Shawn Northrop Nov 20 '17 at 23:48
  • clear var/generation or generated/code depending on your version – Marius Nov 21 '17 at 07:59
  • I get this error on save post Argument 1 passed to Magento\Framework\Exception\LocalizedException::__construct() must be an instance of Magento\Framework\Phrase, string given, called in /[...]/app/code/Altravista/Blog/Controller/Adminhtml/Post/Save.php on line 101 – Altravista Jun 10 '18 at 15:55
  • Should i guess what's on line 101? Also, how is this related to my nswer.....anyway.... you are thriwing an exception that receives as parameter a string when it should receive an instance of Magento\\Framework\\Phrase. How do I know that.... it says so in the error message. Wrap that text in __(...) – Marius Jun 10 '18 at 19:10
  • Thanks for this Marius, I have followed and it almost works for me and image is uploaded i just need to get the filename to save into the database and i get an array to string conversion error. Not sure how to add $imageName to my $data as not sure on the last part of setImageFieldName in my context any ideas? – harri Nov 17 '18 at 15:10
  • Dw i figured it out your an absolute legend! – harri Nov 17 '18 at 15:14
  • Everything works fine, but when edit a form and saved it image save as blank in database how to recover it – Divya Sekar Apr 12 '19 at 13:18
  • it reflects Notice: Undefined variable: data error – Divya Sekar Apr 13 '19 at 10:05
  • Hi Marius, Can you please help with multi upload in the same code. – Ravi Soni Dec 27 '21 at 11:24
3

Admin module for basic add/update news is here. In case you are looking for simple image upload solution look at the following snippet which is taken from the link.

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Backend\App\Action;
protected $_fileUploaderFactory;
public function __construct(
    \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory,
    Action\Context $context     
) {
    $this->_fileUploaderFactory = $fileUploaderFactory;
    parent::__construct($context);
}
public function execute(){
    $uploader = $this->_fileUploaderFactory->create(['fileId' => 'image']);
    $uploader->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']);
    $uploader->setAllowRenameFiles(false);
    $uploader->setFilesDispersion(false);
    $path = $this->_objectManager->get('Magento\Framework\Filesystem')->getDirectoryRead(DirectoryList::MEDIA)
    ->getAbsolutePath('images/');
    $uploader->save($path);
}
Imran Zahoor
  • 291
  • 3
  • 5
1

You could add image field in _prepareForm() method of your admin form:

$fieldset->addField(
    'imagefieldname',
    'image',
    array(
        'name' => 'imagefieldname',
        'label' => __('Image'),
        'title' => __('Image')
    )

  );
Qaisar Satti
  • 32,469
  • 18
  • 85
  • 137
Chandresh P.
  • 435
  • 3
  • 15
0

Check the following link for admin custom form image upload

admin custom form image upload magento 2

Divya Sekar
  • 1,473
  • 1
  • 22
  • 78