Create Product File Type Attribute in Magento 2: – In this blog we are going to create and manage product file type attribute in magento 2.
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)?