Skip to content

Magento 2 : How to save file in media folder using custom product attribute?

I have a create product attribute
But it’s not save file in pub/media folder

-> VendorModule:- MeetanshiProductAttachment
-> attribute_field_name:- custom_product_attachment
-> Media folder path:- custom/product/attachment

MeetanshiProductAttachmentSetupInstallData.php

<?php
namespace MeetanshiProductAttachmentSetup;

use MagentoFrameworkSetupModuleContextInterface;
use MagentoFrameworkSetupModuleDataSetupInterface;
use MagentoFrameworkSetupInstallDataInterface;
use MagentoEavSetupEavSetupFactory;

class InstallData implements InstallDataInterface
{
protected $_eavSetupFactory;

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

public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
    $eavSetup = $this->_eavSetupFactory->create(["setup"=>$setup]);
    $eavSetup->addAttribute(
        MagentoCatalogModelProduct::ENTITY,
        'custom_product_attachment',
        [
            'group' => 'Product Attachment',
            'type' => 'varchar',
            'label' => 'Attachment',
            'input' => 'file',
            'backend' => 'MeetanshiProductAttachmentModelProductAttributeBackendFile',
            'frontend' => '',
            'class' => '',
            'source' => '',
            'global' => MagentoEavModelEntityAttributeScopedAttributeInterface::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
        ]
    );
}
}

MeetanshiProductAttachmentetcadminhtmldi.xml

<?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="MagentoCatalogUiDataProviderProductFormModifierPool" type="MagentoUiDataProviderModifierPool">
    <arguments>
        <argument name="modifiers" xsi:type="array">
            <item name="custom_product_attachment" xsi:type="array">
                <item name="class" xsi:type="string">MeetanshiProductAttachmentUiDataProviderProductFormModifierFile</item>
                <item name="sortOrder" xsi:type="number">1000</item>
            </item>
        </argument>
    </arguments>
</virtualType>
</config>

MeetanshiProductAttachmentModelProductAttributeBackendFile.php

<?php
namespace MeetanshiProductAttachmentModelProductAttributeBackend;

use MagentoFrameworkAppFilesystemDirectoryList;
use MagentoEavModelEntityAttributeBackendAbstractBackend;

class File extends AbstractBackend
{
protected $_file;

protected $_logger;

protected $_filesystem;

protected $_fileUploaderFactory;

public function __construct(
    PsrLogLoggerInterface $logger,
    MagentoFrameworkFilesystem $filesystem,
    MagentoFrameworkFilesystemDriverFile $file,
    MagentoMediaStorageModelFileUploaderFactory $fileUploaderFactory
) {
    $this->_file = $file;
    $this->_filesystem = $filesystem;
    $this->_fileUploaderFactory = $fileUploaderFactory;
    $this->_logger = $logger;
}

public function afterSave($object)
{
    $path = $this->_filesystem->getDirectoryRead(
        DirectoryList::MEDIA
    )->getAbsolutePath(
        'custom/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 {

        $uploader = $this->_fileUpzloaderFactory->create(['fileId' => 'product['.$this->getAttribute()->getName().']']);
        $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() != MagentoMediaStorageModelFileUploader::TMP_NAME_EMPTY) {
            $this->_logger->critical($e);
        }
    }
    return $this;
}
}

MeetanshiProductAttachmentUiDataProviderProductFormModifierFile.php

<?php
namespace MeetanshiProductAttachmentUiDataProviderProductFormModifier;

use MagentoFrameworkStdlibArrayManager;
use MagentoStoreModelStoreManagerInterface;
use MagentoCatalogUiDataProviderProductFormModifierAbstractModifier;

class File extends AbstractModifier
{
protected $arrayManager;

protected $storeManager;

public function __construct(
    ArrayManager $arrayManager,
    StoreManagerInterface $storeManager
) {
    $this->arrayManager = $arrayManager;
    $this->storeManager = $storeManager;
}

public function modifyMeta(array $meta)
{
    $fieldCode = 'custom_product_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(MagentoFrameworkUrlInterface::URL_TYPE_MEDIA);

    $meta = $this->arrayManager->merge(
        $containerPath,
        $meta,
        [
            'children'  => [
                $fieldCode => [
                    'arguments' => [
                        'data' => [
                            'config' => [
                                'elementTmpl'   => 'Meetanshi_ProductAttachment/elements/file',
                                'media_url' => $mediaUrl
                            ],
                        ],
                    ],
                ]
            ]
        ]
    );
    return $meta;
}

public function modifyData(array $data)
{
    return $data;
}
}

MeetanshiProductAttachmentviewadminhtmlwebtemplateelementsfile.html

<input class="admin__control-file" type="file" data-bind="
hasFocus: focused,
attr: {
    name: inputName,
    placeholder: placeholder,
    'aria-describedby': noticeId,
    id: uid,
    disabled: disabled,
    form: formId
}"
/>
<!-- ko if: $parent.source.data.product[code] -->
<span>
<a attr="href: media_url+'custom/product/attachment/'+$parent.source.data.product[code]" text="$parent.source.data.product[code]" target="_blank"></a>
<label attr="for: uid+'_delete'">
    <input type="checkbox" attr="name: 'product['+code + '_delete]', id: uid+'_delete', form: formId">
    <span data-bind="i18n:'Delete'"></span>
</label>
</span>
<!-- /ko -->