Magento2 | PWA | GraphQL

How to attach a file in product admin form and display attachment in product detail page in Magento 2?


In this tutorial, you will learn how to attach a file in product admin form and display attachment in product detail page.

Let's start it by creating custom module.

Find Complete module on Github at Magelearn_Productattachement.

Create folder in app/code/Magelearn/Productattachement

Step 1: Create module registration file

Add registration.php file in it:
<?php
/**
 * Copyright ©  All rights reserved.
 * See COPYING.txt for license details.
 */
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magelearn_Productattachement', __DIR__);
Add composer.json file in it:
{
    "name": "magelearn/module-Productattachement",
    "description": "Magento2 attach a file in product admin form and display attachment in product detail page",
    "type": "magento2-module",
    "license": "OSL-3.0",
    "authors": [
        {
            "email": "info@mage2gen.com",
            "name": "Mage2Gen"
        },
        {
            "email": "vijaymrami@gmail.com",
            "name": "vijay rami"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Magelearn\\Productattachement\\": ""
        }
    }
}
Add etc/module.xml file in it:
<?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="Magelearn_Productattachement" setup_version="1.0.0">
</module> </config>

Step 2: Create InstallData script to add product attachment attribute

Next, we will create a script to add product attribute. So create an InstallData.php in app/code/Magelearn/Productattachement/Setup directory.
<?php
namespace Magelearn\Productattachement\Setup;

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

/**
 * Class InstallData
 * @package Magelearn\Productattachement\Setup
 *
 * @codeCoverageIgnore
 */
class InstallData implements InstallDataInterface
{
    /**
     * @var EavSetupFactory
     */
    protected $_eavSetupFactory;

    public function __construct(
        EavSetupFactory $eavSetupFactory
    ) {
        $this->_eavSetupFactory = $eavSetupFactory;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $eavSetup = $this->_eavSetupFactory->create(["setup"=>$setup]);
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'attachment',
            [
                'group' => 'Product Attachment',
                'type' => 'varchar',
                'label' => 'Attachment',
                'input' => 'file',
                'backend' => 'Magelearn\Productattachement\Model\Product\Attribute\Backend\File',
                'frontend' => '',
                'class' => '',
                'source' => '',
                'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible' => true,
                'required' => false,
                'user_defined' => true,
                'default' => '',
                'searchable' => false,
                'filterable' => false,
                'comparable' => false,
                'visible_on_front' => false,
                'unique' => false,
                'apply_to' => 'simple,configurable', // applicable for simple and configurable product
                'used_in_product_listing' => true
            ]
        );
    }
}
Here attachment is product attribute code.
  • group: Here we have used Product Attachment in the group. So new attribute created in product admin form will be added in a new tab named  Product Attachment.
  • type: Here we will define varchar in type. We will save file name in attribute so we need varchar in type.
  • label: Define the product attribute label, this label will be shown in product admin form.
  • input: Define file to display file type input in product admin form.
  • backend: In this define the backend model path. This backend model will handle the file upload and will save the value of the attribute when saving the product.
  • user_defined: It is custom attribute so will use true in this column.
  • apply_to: In this column define different product type so this attribute will work with only these product types.
  • used_in_product_listing: To use this attribute value in the frontend in product listing set this to true.
  • Next, we will create a backend model file to save uploaded file in the media folder and save the file name to this attribute value.

    Step 3: Create backend model file to upload product attachment file and save value in attachment attribute

    So create File.php in app/code/Magelearn/ProductAttachment/Model/Product/Attribute/Backend directory
    <?php
    namespace Magelearn\Productattachement\Model\Product\Attribute\Backend;
    
    use Magento\Framework\App\Filesystem\DirectoryList;
    
    class File extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
    {
        /**
         * @var \Magento\Framework\Filesystem\Driver\File
         */
        protected $_file;
    
        /**
         * @var \Psr\Log\LoggerInterface
         */
        protected $_logger;
    
        /**
         * @var \Magento\Framework\Filesystem
         */
        protected $_filesystem;
    
        /**
         * @var \Magento\MediaStorage\Model\File\UploaderFactory
         */
        protected $_fileUploaderFactory;
        
    
        /**
         * Construct
         *
         * @param \Psr\Log\LoggerInterface $logger
         * @param \Magento\Framework\Filesystem $filesystem
         * @param \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory
         */
        public function __construct(
            \Psr\Log\LoggerInterface $logger,
            \Magento\Framework\Filesystem $filesystem,
            \Magento\Framework\Filesystem\Driver\File $file,
            \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory
        ) {
            $this->_file = $file;
            $this->_filesystem = $filesystem;
            $this->_fileUploaderFactory = $fileUploaderFactory;
            $this->_logger = $logger;
        }
    
        public function afterSave($object)
        {
            $path = $this->_filesystem->getDirectoryRead(
                DirectoryList::MEDIA
            )->getAbsolutePath(
                'catalog/product/attachment/'
            );
            $delete = $object->getData($this->getAttribute()->getName() . '_delete');
    
            if ($delete) {
                $fileName = $object->getData($this->getAttribute()->getName());
                $object->setData($this->getAttribute()->getName(), '');
                $this->getAttribute()->getEntity()->saveAttribute($object, $this->getAttribute()->getName());
                if ($this->_file->isExists($path.$fileName))  {
                    $this->_file->deleteFile($path.$fileName);
                }
            }
    
            if (empty($_FILES['product']['tmp_name'][$this->getAttribute()->getName()])) {
                return $this;
            }
    
            try {
                /** @var $uploader \Magento\MediaStorage\Model\File\Uploader */
                $uploader = $this->_fileUploaderFactory->create(['fileId' => 'product['.$this->getAttribute()->getName().']']);
                $uploader->setAllowedExtensions(['pdf']);
                $uploader->setAllowRenameFiles(true);
                $result = $uploader->save($path);
                $object->setData($this->getAttribute()->getName(), $result['file']);
                $this->getAttribute()->getEntity()->saveAttribute($object, $this->getAttribute()->getName());
            } catch (\Exception $e) {
                if ($e->getCode() != \Magento\MediaStorage\Model\File\Uploader::TMP_NAME_EMPTY) {
                    $this->_logger->critical($e);
                }
            }
            
            return $this;
        }
    }
    Now you can see the product attachment tab and input type file attribute in a new tab in product admin form.

    Now after attaching file and save the product, the file is uploaded in pub/media/catalog/product/attachment directory and file name stored in attachment attribute value.

    Step 4: Display product attachment link and delete checkbox after saving product in admin form

    But after saving the product in product attachment tab file name is not showing up or how to remove file checkbox is not showing up.

    So we will display an anchor tag that contains the file path and we will also add delete checkbox to remove the file in the product attachment tab.

    We will use a modifier to add these changes in product attachment tab.

    So we will create a di.xml in app/code/Magelearn/Productattachement/etc/adminhtml directory.
    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
       <virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool" type="Magento\Ui\DataProvider\Modifier\Pool">
           <arguments>
               <argument name="modifiers" xsi:type="array">
                   <item name="attachment" xsi:type="array">
                       <item name="class" xsi:type="string">Magelearn\Productattachement\Ui\DataProvider\Product\Form\Modifier\File</item>
                       <item name="sortOrder" xsi:type="number">1000</item>
                   </item>
               </argument>
           </arguments>
       </virtualType>
    </config>
    Next, we will create File.php in app/code/Magelearn/Productattachement/Ui/DataProvider/Product/Form/Modifier directory.
    <?php
    namespace Magelearn\Productattachement\Ui\DataProvider\Product\Form\Modifier;
     
    use Magento\Framework\Stdlib\ArrayManager;
    use Magento\Store\Model\StoreManagerInterface;
    use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
     
    class File extends AbstractModifier
    {
        /**
         * @var ArrayManager
         */
        protected $arrayManager;
    
        /**
         * @var StoreManagerInterface
         */
        protected $storeManager;
     
        /**
         * @param ArrayManager $arrayManager
         * @param StoreManagerInterface $storeManager
         */
        public function __construct(
            ArrayManager $arrayManager,
            StoreManagerInterface $storeManager
        ) {
            $this->arrayManager = $arrayManager;
            $this->storeManager = $storeManager;
        }
     
        public function modifyMeta(array $meta)
        {
            $fieldCode = 'attachment';
            $elementPath = $this->arrayManager->findPath($fieldCode, $meta, null, 'children');
            $containerPath = $this->arrayManager->findPath(static::CONTAINER_PREFIX . $fieldCode, $meta, null, 'children');
     
            if (!$elementPath) {
                return $meta;
            }
    		
     		$mediaUrl =  $this->storeManager->getStore()
                ->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA);
    		
    		$attachementUrl =  $mediaUrl . 'catalog/product/attachment/';
    			
            $meta = $this->arrayManager->merge(
                $containerPath,
                $meta,
                [
                    'children'  => [
                        $fieldCode => [
                            'arguments' => [
                                'data' => [
                                    'config' => [
                                        'elementTmpl'   => 'Magelearn_Productattachement/elements/file',
                                        'attachment_url' => $attachementUrl
                                    ],
                                ],
                            ],
                        ]
                    ]
                ]
            );
            return $meta;
        }
     
        /**
         * {@inheritdoc}
         */
        public function modifyData(array $data)
        {
            return $data;
        }
    }
    We have defined an HTML template file in the modifier. So next we will create file.html in app/code/Magelearn/Productattachement/view/adminhtml/web/template/elements diectory.
    
    
    
        
        
    
    
    


    Step 5: Display product attachment in product detail page in frontend

    Next, we will create a new product attachment tab in the product detail page and will show product attachment for that product.

    So next we will create a catalog_product_view.xml in app/code/Magelearn/Productattachement/view/frontend/layout directory.
    
    
        
            
                
                       
                    	Product Attachment
                 
                
            
        
    
    
    As we have defined in layout file we will create block and phtml file. So next we will create an Downloads.php in app/code/Magelearn/Productattachement/Block/Product/ directory.
    <?php
    namespace Magelearn\Productattachement\Block\Product;
    
    use Magento\Framework\View\Element\Template;
    
    /**
     * Class:Downloads
     * Magelearn\Productattachement\Block
     *
     * @author      Sebwite
     * @package     Magelearn\Productattachement
     * @copyright   Copyright (c) 2015, Sebwite. All rights reserved
     */
    class Downloads extends Template
    {
        /**
         * @var \Magento\Framework\Registry
         */
        private $coreRegistry;
    
        /**
         * @param \Magento\Framework\View\Element\Template\Context $context
         * @param \Magento\Framework\Registry                      $coreRegistry
         */
        public function __construct(
                \Magento\Framework\View\Element\Template\Context $context,
                \Magento\Framework\Registry $coreRegistry,
                \Magento\Store\Model\StoreManagerInterface $storeManager
        )
        {
            $this->coreRegistry = $coreRegistry;
            $this->_storeManager = $storeManager;
            parent::__construct($context);
        }
    
        /**
         * Return Downloads
         *
         * @return mixed
         */
        public function getProductAttachment()
        {
            return $this->getProduct()->getAttachment();
        }
    
        /**
         * Return current product instance
         *
         * @return \Magento\Catalog\Model\Product
         */
        public function getProduct()
        {
            return $this->coreRegistry->registry('product');
        }
    
        /**
         *
         * @return string
         */
        public function getAttachmentUrl()
        {
            // @TODO - index.php weghalen
            $mediaUrl = $this ->_storeManager-> getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA );
            return $mediaUrl . 'catalog/product/attachment/' . $this->getProduct()->getAttachment();
        }
    }
    
    Next we will create a downloads.phtml in app/code/Magelearn/Productattachement/view/frontend/templates directory.
    <?php 
    $product = $block->getProduct();
    //$product_attachment = $product->getAttachment();
    $product_attachment = $block->getProductAttachment();
    ?>
    <?php if(!empty($product_attachment)) : ?>
    
    <?php endif; ?>
    Now flush cache and open product detail page. Now you can see the product attachment tab.




    0 Comments On "How to attach a file in product admin form and display attachment in product detail page in Magento 2?"

    Back To Top