I’m working on a dynamic product row attribute in Magento 2, and I’ve successfully added multiple fields. Now, I want to include a video uploader field in the product form. The text fields are displaying correctly, as shown in the attached screenshot. However, I’m unsure how to add a video uploader (file uploader) field to the form.
Here’s the code I’m trying: UiDataProviderProductFormModifierDynamicRowAttribute.php
'video_upload' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => 'field',
'formElement' => 'fileUploader',
'dataType' => 'text',
'label' => __('Video Upload'),
'dataScope' => 'video_upload',
'uploaderConfig' => [
'url' => $this->urlBuilder->getUrl('adminhtml/media/upload'), // Using $this->urlBuilder
],
'allowedExtensions' => ['mp4', 'avi', 'mkv'],
'maxFileSize' => 1024 * 1000 * 1000, // 1GB max file size
'sortOrder' => 40,
],
],
],
],
Here is complete code of this file:
<?php
namespace CICoursePreviewUiDataProviderProductFormModifier;
use MagentoCatalogModelLocatorLocatorInterface;
use MagentoCatalogUiDataProviderProductFormModifierAbstractModifier;
use MagentoUiComponentFormField;
use MagentoUiComponentFormElementInput;
use MagentoUiComponentDynamicRows;
use MagentoUiComponentContainer;
use MagentoUiComponentFormElementDataTypeText;
use MagentoEavModelResourceModelEntityAttributeSetCollectionFactory as AttributeSetCollection;
use MagentoFrameworkStdlibArrayManager;
use MagentoFrameworkSerializeSerializerInterface;
use PsrLogLoggerInterface;
class DynamicRowAttribute extends AbstractModifier
{
public const PRODUCT_ATTRIBUTE_CODE = 'course_previews';
public const FIELD_IS_DELETE = 'is_delete';
public const FIELD_SORT_ORDER_NAME = 'sort_order';
private $locator;
private $attributeSetCollection;
private $serializer;
private $arrayManager;
private $logger;
/**
* Dependency Initialization
*
* @param LocatorInterface $locator
* @param AttributeSetCollection $attributeSetCollection
* @param SerializerInterface $serializer
* @param ArrayManager $arrayManager
* @param LoggerInterface $logger
*/
public function __construct(
LocatorInterface $locator,
AttributeSetCollection $attributeSetCollection,
SerializerInterface $serializer,
ArrayManager $arrayManager,
LoggerInterface $logger
) {
$this->locator = $locator;
$this->attributeSetCollection = $attributeSetCollection;
$this->serializer = $serializer;
$this->arrayManager = $arrayManager;
$this->logger = $logger;
}
/**
* Modify Data
*
* @param array $data
* @return array
*/
public function modifyData(array $data)
{
$this->logger->info('Modify Data Called');
$fieldCode = self::PRODUCT_ATTRIBUTE_CODE;
$model = $this->locator->getProduct();
$modelId = $model->getId();
$this->logger->info('Product ID: ' . $modelId);
$coursePreviewsData = $model->getData(self::PRODUCT_ATTRIBUTE_CODE);
if ($coursePreviewsData) {
$this->logger->info('Course Previews Data from Product: ' . json_encode($coursePreviewsData));
$coursePreviewsData = $this->serializer->unserialize($coursePreviewsData, true);
$path = $modelId . '/' . self::DATA_SOURCE_DEFAULT . '/' . $fieldCode;
$data = $this->arrayManager->set($path, $data, $coursePreviewsData);
} else {
$this->logger->info('Course Previews Data is empty');
}
return $data;
}
/**
* Modify Meta
*
* @param array $meta
* @return array
*/
public function modifyMeta(array $meta)
{
$this->logger->info('Modify Meta Called');
// Log the entire meta array before searching for the path
//$this->logger->info('Full Meta Array Before Path Search: ' . json_encode($meta));
// Find the path for course previews in the meta structure
$coursePreviewsPath = $this->arrayManager->findPath(
self::PRODUCT_ATTRIBUTE_CODE,
$meta,
null,
'children'
);
// Log the result of the findPath call
$this->logger->info('Find Path Result for Course Previews: ' . json_encode($coursePreviewsPath));
if ($coursePreviewsPath) {
$this->logger->info('Course Previews Path Found: ' . $coursePreviewsPath);
// Merge and update meta with the dynamic row field structure
$meta = $this->arrayManager->merge(
$coursePreviewsPath,
$meta,
$this->initDynamicRowFieldStructure($meta, $coursePreviewsPath)
);
// Set and remove original path if needed
$meta = $this->arrayManager->set(
$this->arrayManager->slicePath($coursePreviewsPath, 0, -3)
. '/' . self::PRODUCT_ATTRIBUTE_CODE,
$meta,
$this->arrayManager->get($coursePreviewsPath, $meta)
);
$meta = $this->arrayManager->remove(
$this->arrayManager->slicePath($coursePreviewsPath, 0, -2),
$meta
);
} else {
// Log that the path was not found and output the meta structure
$this->logger->info('Course Previews Path Not Found. Full Meta: ' . json_encode($meta));
}
return $meta;
}
/**
* Add dynamic rows for course previews
*
* @param int $sortOrder
* @return array
*/
protected function addDynamicRowConfig($sortOrder)
{
return [
'arguments' => [
'data' => [
'config' => [
'addButtonLabel' => __('Add Course Preview'),
'componentType' => DynamicRows::NAME,
'component' => 'Magento_Ui/js/dynamic-rows/dynamic-rows',
'additionalClasses' => 'admin__field-wide',
'deleteProperty' => static::FIELD_IS_DELETE,
'deleteValue' => '1',
'renderDefaultRecord' => false,
'sortOrder' => $sortOrder,
],
],
],
'children' => [
'record' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Container::NAME,
'component' => 'Magento_Ui/js/dynamic-rows/record',
'positionProvider' => static::FIELD_SORT_ORDER_NAME,
'isTemplate' => true,
'is_collection' => true,
],
],
],
'children' => [
'title' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Field::NAME,
'formElement' => Input::NAME,
'dataType' => Text::NAME,
'label' => __('Video Title'),
'dataScope' => 'title',
'sortOrder' => 10,
'validation' => [
'required-entry' => true,
],
],
],
],
],
'description' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Field::NAME,
'formElement' => Input::NAME,
'dataType' => Text::NAME,
'label' => __('Video Description'),
'dataScope' => 'description',
'sortOrder' => 20,
],
],
],
],
'video_url' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Field::NAME,
'formElement' => Input::NAME,
'dataType' => Text::NAME,
'label' => __('Video URL'),
'dataScope' => 'video_url',
'sortOrder' => 30,
],
],
],
],
'video_upload' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Field::NAME,
'formElement' => Input::NAME,
'dataType' => Text::NAME,
'label' => __('Video Upload'),
'dataScope' => 'video_upload',
'sortOrder' => 40,
],
],
],
],
'actionDelete' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => 'actionDelete',
'dataType' => Text::NAME,
'label' => '',
'sortOrder' => 50,
],
],
],
],
]
]
]
];
}
/**
* Initialize dynamic row field structure
*
* @param array $meta
* @param string $coursePreviewsPath
* @return array
*/
protected function initDynamicRowFieldStructure($meta, $coursePreviewsPath)
{
return [
'arguments' => [
'data' => [
'config' => [
'componentType' => 'dynamicRows',
'label' => __('Course Previews'),
'renderDefaultRecord' => false,
'recordTemplate' => 'record',
'dataScope' => '',
'dndConfig' => [
'enabled' => false,
],
'disabled' => false,
'sortOrder' => $this->arrayManager->get($coursePreviewsPath . '/arguments/data/config/sortOrder', $meta),
],
],
],
'children' => [
'record' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Container::NAME,
'isTemplate' => true,
'is_collection' => true,
'component' => 'Magento_Ui/js/dynamic-rows/record',
'dataScope' => '',
],
],
],
'children' => [
'title' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Video Title'),
'dataScope' => 'title',
],
],
],
],
'description' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Video Description'),
'dataScope' => 'description',
],
],
],
],
'video_url' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Video URL'),
'dataScope' => 'video_url',
],
],
],
],
'video_upload' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Video Upload'),
'dataScope' => 'video_upload',
],
],
],
],
'actionDelete' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => 'actionDelete',
'dataType' => Text::NAME,
'label' => '',
],
],
],
],
]
],
],
];
}
}