In this blog, we’ll explain how to Create Product File Type Attribute in Magento 2, covering setup steps and practical use cases for file uploads.
Follow the below steps in order to create file type attribute:
Step 1:
First of all we have to create a product attribute with input type “File”. Here I’m creating it by installer. So, create a InstallData.php file in /app/code/Vendor/Module/Setup directory and copy below code.
<?php
namespace Vendor\Module\Setup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Eav\Model\Entity\Attribute\Set as AttributeSet;
use Magento\Catalog\Model\ResourceModel\Product as ResourceProduct;
class InstallData implements InstallDataInterface
{
protected $_attributeSet;
protected $_eavSetupFactory;
protected $_resourceProduct;
public function __construct(
AttributeSet $attributeSet,
EavSetupFactory $eavSetupFactory,
ResourceProduct $resourceProduct
) {
$this->_attributeSet = $attributeSet;
$this->_eavSetupFactory = $eavSetupFactory;
$this->_resourceProduct = $resourceProduct;
}
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) {
$eavSetup = $this->_eavSetupFactory->create(["setup"=>$setup]);
$eavSetup->addAttribute(
\Magento\Catalog\Model\Product::ENTITY,
'agreement_file',
[
'type' => 'varchar',
'label' => 'Agreement File',
'input' => 'file',
'backend' => 'Vendor\Module\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' => false
]
);
// assign attribute to attribute set
$entityType = $this->_resourceProduct->getEntityType();
$attributeSetCollection = $this->_attributeSet->setEntityTypeFilter($entityType);
foreach ($attributeSetCollection as $attributeSet) {
$eavSetup->addAttributeToSet("catalog_product", $attributeSet->getAttributeSetName(), "General", "agreement_file");
}
}
}
Step 2:
Then create backend model File.php(which we have defined in attribute options) in app/code/Vendor/Module/Model/Product/Attribute/Backend directory and copy below code.
<?php
namespace Vendor\Module\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/file/'
);
$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)) {
return $this;// if no image is set then nothing to do
}
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;
}
}
Step 3:
To display saved file, we need to define template for the attribute. So, first define modifier class of the attribute in app/code/Vendor/Module/etc/adminhtml/di.xml as mentioned below
<?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">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="agreement_file" xsi:type="array">
<item name="class" xsi:type="string">Vendor\Module\Ui\DataProvider\Product\Form\Modifier\File</item>
<item name="sortOrder" xsi:type="number">10</item>
</item>
</argument>
</arguments>
</virtualType>
</config>
Step 4:
Create modifier class File.php in app/code/Vendor/Module/Ui/DataProvider/Product/Form/Modifier directory and copy below code
<?php
namespace Vendor\Module\Ui\DataProvider\Product\Form\Modifier;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
class File extends AbstractModifier
{
/**
* @var Magento\Framework\Stdlib\ArrayManager
*/
protected $arrayManager;
/**
* @param ArrayManager $arrayManager
*/
public function __construct(
ArrayManager $arrayManager
) {
$this->arrayManager = $arrayManager;
}
public function modifyMeta(array $meta)
{
$fieldCode = 'agreement_file';
$elementPath = $this->arrayManager->findPath($fieldCode, $meta, null, 'children');
$containerPath = $this->arrayManager->findPath(static::CONTAINER_PREFIX . $fieldCode, $meta, null, 'children');
if (!$elementPath) {
return $meta;
}
$meta = $this->arrayManager->merge(
$containerPath,
$meta,
[
'children' => [
$fieldCode => [
'arguments' => [
'data' => [
'config' => [
'elementTmpl' => 'Vendor_Module/elements/file',
],
],
],
]
]
]
);
return $meta;
}
/**
* {@inheritdoc}
*/
public function modifyData(array $data)
{
return $data;
}
}
Step 5:
At last create template file file.html in /app/code/Vendor/Module/view/adminhtml/web/template/elements directory and write below code
<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: '/pub/media/catalog/product/file/'+$parent.source.data.product[code]" text="$parent.source.data.product[code]"></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 -->

In this way we can create file type product attribute in magento 2. Thanks.. 🙂
For complete module please find at https://github.com/rani-webkul/product-file-attribute.
What if we want to integrate this into webkul marketplace and allow seller to add files when creating products in their dashboard (using the custom attribute add-on also)?