I have created a custom category thumbnail attribute according to the post (Magento 2 create category attribute thumbnail and upload image using File Uploader Component).
The image gets saved in the "tmp" directory but is not saved when I save the category.
what is the issue for that?
- 
 1Thanks you ! but alert "Atention The file was not uploaded." Help me!River– River2016年09月13日 18:33:09 +00:00Commented Sep 13, 2016 at 18:33
- 
 @Dreams clear var/generation folder and run setup:di:compile or enable developer modenethead– nethead2016年09月16日 09:03:34 +00:00Commented Sep 16, 2016 at 9:03
1 Answer 1
This is quite a big answer.
In Magento 2.1.0 you need to apply a patch to create a additional file upload attribute for category.
As they are using hardcoded values for image attribute on many places. for more details have a look at link1 and link2
Steps to apply a patch.
1) Edit composer.json file located at root directory as follows.
{
 "name": "magento/project-community-edition",
 "description": "eCommerce Platform for Growth (Community Edition)",
 "type": "project",
 "version": "2.1.0",
 "license": [
 "OSL-3.0",
 "AFL-3.0"
 ],
 "repositories": [
 {
 "type": "composer",
 "url": "https://repo.magento.com/"
 }
 ],
 "require": {
 "magento/product-community-edition": "2.1.0",
 "cweagans/composer-patches": "~1.4.0",
 "composer/composer": "@alpha" 
 },
 "require-dev": {
 "phpunit/phpunit": "4.1.0",
 "squizlabs/php_codesniffer": "1.5.3",
 "phpmd/phpmd": "2.3.*",
 "pdepend/pdepend": "2.2.2",
 "sjparkinson/static-review": "~4.1",
 "fabpot/php-cs-fixer": "~1.2",
 "lusitanian/oauth": "~0.3 <=0.7.0"
 },
 "config": {
 "use-include-path": true
 },
 "autoload": {
 "psr-4": {
 "Magento\\Framework\\": "lib/internal/Magento/Framework/",
 "Magento\\Setup\\": "setup/src/Magento/Setup/",
 "Magento\\": "app/code/Magento/"
 },
 "psr-0": {
 "": "app/code/"
 },
 "files": [
 "app/etc/NonComposerComponentRegistration.php"
 ]
 },
 "autoload-dev": {
 "psr-4": {
 "Magento\\Sniffs\\": "dev/tests/static/framework/Magento/Sniffs/",
 "Magento\\Tools\\": "dev/tools/Magento/Tools/",
 "Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/",
 "Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/",
 "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/"
 }
 },
 "minimum-stability": "alpha",
 "prefer-stable": true,
 "extra": {
 "patches": {
 "magento/module-catalog": {
 "Fix: https://github.com/magento/magento2/issues/5438": "patches/Patch-Magento_Catalog-M2.1.0-image-attribute-backend-model-hardcoded-attribute-code-removal.patch"
 },
 "magento/module-ui": {
 "Fix: https://github.com/magento/magento2/issues/5438": "patches/Patch-Magento_Ui-M2.1.0-allow-backend-to-know-the-origin-input-of-the-upload-request.patch"
 }
 },
 "magento-force": "override"
 }
}
As mentioned on link2 above newly added code is in extra and require.
2) Create a patches folder on root directory.
3) Create file Patch-Magento_Catalog-M2.1.0-image-attribute-backend-model-hardcoded-attribute-code-removal.patch under patches folder with following contains.
--- Controller/Adminhtml/Category/Save.php.org 2016年08月03日 21:35:33.772109252 +0200
+++ Controller/Adminhtml/Category/Save.php 2016年08月03日 21:36:33.982229407 +0200
@@ -6,6 +6,7 @@
 namespace Magento\Catalog\Controller\Adminhtml\Category;
 use Magento\Store\Model\StoreManagerInterface;
+use \Magento\Catalog\Api\Data\CategoryAttributeInterface;
 /**
 * Class Save
@@ -49,6 +50,11 @@
 private $storeManager;
 /**
+ * @var \Magento\Eav\Model\Config
+ */
+ private $eavConfig;
+
+ /**
 * Constructor
 *
 * @param \Magento\Backend\App\Action\Context $context
@@ -62,37 +68,15 @@
 \Magento\Framework\Controller\Result\RawFactory $resultRawFactory,
 \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
 \Magento\Framework\View\LayoutFactory $layoutFactory,
- StoreManagerInterface $storeManager
+ StoreManagerInterface $storeManager,
+ \Magento\Eav\Model\Config $eavConfig
 ) {
 parent::__construct($context);
 $this->resultRawFactory = $resultRawFactory;
 $this->resultJsonFactory = $resultJsonFactory;
 $this->layoutFactory = $layoutFactory;
 $this->storeManager = $storeManager;
- }
-
- /**
- * Filter category data
- *
- * @param array $rawData
- * @return array
- */
- protected function _filterCategoryPostData(array $rawData)
- {
- $data = $rawData;
- // @todo It is a workaround to prevent saving this data in category model and it has to be refactored in future
- if (isset($data['image']) && is_array($data['image'])) {
- if (!empty($data['image']['delete'])) {
- $data['image'] = null;
- } else {
- if (isset($data['image'][0]['name']) && isset($data['image'][0]['tmp_name'])) {
- $data['image'] = $data['image'][0]['name'];
- } else {
- unset($data['image']);
- }
- }
- }
- return $data;
+ $this->eavConfig = $eavConfig;
 }
 /**
@@ -126,7 +110,7 @@
 $this->storeManager->setCurrentStore($store->getCode());
 $parentId = isset($categoryPostData['parent']) ? $categoryPostData['parent'] : null;
 if ($categoryPostData) {
- $category->addData($this->_filterCategoryPostData($categoryPostData));
+ $category->addData($categoryPostData);
 if ($isNewCategory) {
 $parentCategory = $this->getParentCategory($parentId, $storeId);
 $category->setPath($parentCategory->getPath());
@@ -247,22 +231,29 @@
 );
 }
- /**
- * Image data preprocessing
- *
- * @param array $data
- *
- * @return array
- */
 public function imagePreprocessing($data)
 {
- if (empty($data['image'])) {
- unset($data['image']);
- $data['image']['delete'] = true;
+ $entityType = $this->eavConfig->getEntityType(CategoryAttributeInterface::ENTITY_TYPE_CODE);
+
+ foreach ($entityType->getAttributeCollection() as $attributeModel) {
+ $attributeCode = $attributeModel->getAttributeCode();
+ $backendModel = $attributeModel->getBackend();
+
+ if (isset($data[$attributeCode])) {
+ continue;
+ }
+
+ if (!$backendModel instanceof \Magento\Catalog\Model\Category\Attribute\Backend\Image) {
+ continue;
+ }
+
+ $data[$attributeCode] = false;
 }
+
 return $data;
 }
+
 /**
 * Converting inputs from string to boolean
 *
--- Controller/Adminhtml/Category/Image/Upload.php.org 2016年08月01日 20:36:22.014237780 +0200
+++ Controller/Adminhtml/Category/Image/Upload.php 2016年08月01日 20:36:25.292475257 +0200
@@ -50,8 +50,10 @@
 */
 public function execute()
 {
+ $imageId = $this->_request->getParam('param_name', 'image');
+
 try {
- $result = $this->imageUploader->saveFileToTmpDir('image');
+ $result = $this->imageUploader->saveFileToTmpDir($imageId);
 $result['cookie'] = [
 'name' => $this->_getSession()->getName(),
--- Model/Category/Attribute/Backend/Image.php.org 2016年08月03日 21:36:05.695112561 +0200
+++ Model/Category/Attribute/Backend/Image.php 2016年08月03日 21:36:46.829828298 +0200
@@ -13,6 +13,8 @@
 class Image extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
 {
+ const ADDITIONAL_DATA_SUFFIX = '_additional_data';
+
 /**
 * @var \Magento\MediaStorage\Model\File\UploaderFactory
 *
@@ -21,8 +23,6 @@
 protected $_uploaderFactory;
 /**
- * Filesystem facade
- *
 * @var \Magento\Framework\Filesystem
 *
 * @deprecated
@@ -30,8 +30,6 @@
 protected $_filesystem;
 /**
- * File Uploader factory
- *
 * @var \Magento\MediaStorage\Model\File\UploaderFactory
 *
 * @deprecated
@@ -46,15 +44,11 @@
 protected $_logger;
 /**
- * Image uploader
- *
 * @var \Magento\Catalog\Model\ImageUploader
 */
 private $imageUploader;
 /**
- * Image constructor.
- *
 * @param \Psr\Log\LoggerInterface $logger
 * @param \Magento\Framework\Filesystem $filesystem
 * @param \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory
@@ -70,8 +64,50 @@
 }
 /**
- * Get image uploader
+ * @param $value
+ * @return string|bool
+ */
+ protected function getUploadedImageName($value)
+ {
+ if (!is_array($value)) {
+ return false;
+ }
+
+ if (!count($value)) {
+ return false;
+ }
+
+ $imageData = reset($value);
+
+ if (!isset($imageData['name'])) {
+ return false;
+ }
+
+ return $imageData['name'];
+ }
+
+ /**
+ * Avoiding saving potential upload data to DB
 *
+ * @param \Magento\Framework\DataObject $object
+ * @return $this
+ */
+ public function beforeSave($object)
+ {
+ $attributeName = $this->getAttribute()->getName();
+ $value = $object->getData($attributeName);
+
+ if ($value === false || (is_array($value) && isset($value['delete']) && $value['delete'] === true)) {
+ $object->setData($attributeName, '');
+ } else if ($imageName = $this->getUploadedImageName($value)) {
+ $object->setData($attributeName . self::ADDITIONAL_DATA_SUFFIX, $value);
+ $object->setData($attributeName, $imageName);
+ }
+
+ return parent::beforeSave($object);
+ }
+
+ /**
 * @return \Magento\Catalog\Model\ImageUploader
 *
 * @deprecated
@@ -80,9 +116,10 @@
 {
 if ($this->imageUploader === null) {
 $this->imageUploader = \Magento\Framework\App\ObjectManager::getInstance()->get(
- 'Magento\Catalog\CategoryImageUpload'
+ \Magento\Catalog\CategoryImageUpload::class
 );
 }
+
 return $this->imageUploader;
 }
@@ -94,15 +131,18 @@
 */
 public function afterSave($object)
 {
- $image = $object->getData($this->getAttribute()->getName(), null);
+ $value = $object->getData($this->getAttribute()->getName() . self::ADDITIONAL_DATA_SUFFIX);
- if ($image !== null) {
- try {
- $this->getImageUploader()->moveFileFromTmp($image);
- } catch (\Exception $e) {
- $this->_logger->critical($e);
- }
+ if (!$imageName = $this->getUploadedImageName($value)) {
+ return $this;
 }
+
+ try {
+ $this->getImageUploader()->moveFileFromTmp($imageName);
+ } catch (\Exception $e) {
+ $this->_logger->critical($e);
+ }
+
 return $this;
 }
 }
--- Model/Category/DataProvider.php.org 2016年08月01日 21:35:43.567609510 +0200
+++ Model/Category/DataProvider.php 2016年08月01日 21:43:07.800993338 +0200
@@ -203,14 +203,24 @@
 $category = $this->getCurrentCategory();
 if ($category) {
 $categoryData = $category->getData();
+
 $categoryData = $this->addUseDefaultSettings($category, $categoryData);
 $categoryData = $this->addUseConfigSettings($categoryData);
 $categoryData = $this->filterFields($categoryData);
- if (isset($categoryData['image'])) {
- unset($categoryData['image']);
- $categoryData['image'][0]['name'] = $category->getData('image');
- $categoryData['image'][0]['url'] = $category->getImageUrl();
+
+ foreach ($category->getAttributes() as $attributeCode => $attribute) {
+ $backendModel = $attribute->getBackend();
+
+ if ($backendModel instanceof \Magento\Catalog\Model\Category\Attribute\Backend\Image) {
+ if (isset($categoryData[$attributeCode])) {
+ unset($categoryData[$attributeCode]);
+
+ $categoryData[$attributeCode][0]['name'] = $category->getData($attributeCode);
+ $categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode);
+ }
+ }
 }
+
 $this->loadedData[$category->getId()] = $categoryData;
 }
 return $this->loadedData;
--- Model/Category.php.org 2016年08月01日 21:41:47.535876208 +0200
+++ Model/Category.php 2016年08月01日 21:42:57.916422400 +0200
@@ -652,14 +652,15 @@
 }
 /**
- * Retrieve image URL
+ * @param $attributeCode
 *
- * @return string
+ * @return bool|string
+ * @throws \Magento\Framework\Exception\LocalizedException
 */
- public function getImageUrl()
+ public function getImageUrl($attributeCode = 'image')
 {
 $url = false;
- $image = $this->getImage();
+ $image = $this->getData($attributeCode);
 if ($image) {
 if (is_string($image)) {
 $url = $this->_storeManager->getStore()->getBaseUrl(
4) Create file Patch-Magento_Ui-M2.1.0-allow-backend-to-know-the-origin-input-of-the-upload-request.patch under patches folder with following contains.
--- view/base/web/js/form/element/file-uploader.js.org 2016年08月01日 20:33:22.866555339 +0200
+++ view/base/web/js/form/element/file-uploader.js 2016年08月01日 20:33:42.384061881 +0200
@@ -302,7 +302,13 @@
 allowed = this.isFileAllowed(file);
 if (allowed.passed) {
- $(e.target).fileupload('process', data).done(function () {
+ var $target = $(e.target);
+
+ $target.on('fileuploadsend', function(event, postData) {
+ postData.data.set('param_name', this.paramName);
+ }.bind(data));
+
+ $target.fileupload('process', data).done(function () {
 data.submit();
 });
 } else {
5) Run following Commands
- composer validate composer.json (To Vallidate composer file).
- composer update
- php bin/magento setup:upgrade
6) Now its time to create your module for new file upload option.
Create a file
app\code\Vendor\Module\registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
 \Magento\Framework\Component\ComponentRegistrar::MODULE,
 "Vendor_Module",
 __DIR__
 );
Create a file
app\code\Vendor\Module\etc\module.xml
<?xml version="1.0" encoding="UTF-8" ?>
 <config>
 <module name="Vendor_Module" setup_version="1.0" />
 </config>
Create a file
app\code\Vendor\Module\Setup\InstallData.php
<?php
 namespace Vendor\Module\Setup;
 use Magento\Framework\Module\Setup\Migration;
 use Magento\Framework\Setup\InstallDataInterface;
 use Magento\Framework\Setup\ModuleContextInterface;
 use Magento\Framework\Setup\ModuleDataSetupInterface;
 use Magento\Catalog\Setup\CategorySetupFactory;
 class InstallData implements InstallDataInterface
 {
 public function __construct(CategorySetupFactory $categorySetupFactory)
 {
 $this->categorySetupFactory = $categorySetupFactory;
 }
 public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
 {
 $installer = $setup;
 $installer->startSetup(); 
 $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]);
 $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Category::ENTITY);
 $attributeSetId = $categorySetup->getDefaultAttributeSetId($entityTypeId);
 $categorySetup->removeAttribute(
 \Magento\Catalog\Model\Category::ENTITY, 'featured_image' );
 $categorySetup->addAttribute(
 \Magento\Catalog\Model\Category::ENTITY, 'featured_image', [
 'type' => 'varchar',
 'label' => 'Featured Image',
 'input' => 'image',
 'backend' => 'Magento\Catalog\Model\Category\Attribute\Backend\Image',
 'required' => false,
 'sort_order' => 5,
 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
 'group' => 'General Information',
 ]
 );
 $installer->endSetup();
 }
 }
Create a file
app\code\Vendor\Module\view\adminhtml\ui_component\category_form.xml
<?xml version="1.0" encoding="UTF-8" ?>
 <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
 <fieldset name="content">
 <argument name="data" xsi:type="array">
 <item name="config" xsi:type="array">
 <item name="label" xsi:type="string" translate="true">Content</item>
 <item name="collapsible" xsi:type="boolean">true</item>
 <item name="sortOrder" xsi:type="number">10</item>
 </item>
 </argument>
 <field name="featured_image">
 <argument name="data" xsi:type="array">
 <item name="config" xsi:type="array">
 <item name="dataType" xsi:type="string">string</item>
 <item name="source" xsi:type="string">category</item>
 <item name="label" xsi:type="string" translate="true">Featured Image</item>
 <item name="visible" xsi:type="boolean">true</item>
 <item name="formElement" xsi:type="string">fileUploader</item>
 <item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item>
 <item name="previewTmpl" xsi:type="string">Magento_Catalog/image-preview</item>
 <item name="required" xsi:type="boolean">false</item>
 <item name="sortOrder" xsi:type="number">40</item>
 <item name="uploaderConfig" xsi:type="array">
 <item name="url" xsi:type="url" path="catalog/category_image/upload"/>
 </item>
 </item>
 </argument>
 </field>
 </fieldset>
 </form>
Hope this helps you.
- 
 Thanks for the answer, I will update after I've tried this out.V.S– V.S2016年08月19日 05:01:28 +00:00Commented Aug 19, 2016 at 5:01
- 
 this is for 2.1.0 are you using this version ?Ashish Madankar M2 Professiona– Ashish Madankar M2 Professiona2016年08月19日 05:23:40 +00:00Commented Aug 19, 2016 at 5:23
- 
 Yes, I also listed this in the question title.V.S– V.S2016年08月19日 05:34:42 +00:00Commented Aug 19, 2016 at 5:34
- 
 @KandarpBPatel thankx for the suggestion will try it soon.Ashish Madankar M2 Professiona– Ashish Madankar M2 Professiona2016年12月05日 01:10:09 +00:00Commented Dec 5, 2016 at 1:10