Wie speichere ich ein benutzerdefiniertes Feld in einer benutzerdefinierten Datenbanktabelle, während ich das Produkt aus dem Back-End bearbeite?

11

Ich habe ein benutzerdefiniertes Modul erstellt, um die benutzerdefinierte Registerkarte im Produktformular im Back-End anzuzeigen. Ich habe diese Lösung verwendet.

Jetzt auf der Registerkarte füge ich benutzerdefinierte Felder hinzu, um sie in einer benutzerdefinierten Datenbanktabelle zu speichern. sagen<input type="text" name="my_new_field" value="123">

Außerdem wurde ein benutzerdefinierter Controller für das Speichern von Administratorprodukten erstellt (siehe unten).

In etc / di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Controller\Adminhtml\Product\Save" type="Namespace\Module\Controller\Adminhtml\Rewrite\Product\Save" />
</config>

Und in Controller / Adminhtml / Rewrite / Product / Save.php

<?php

    namespace Namespace\Module\Controller\Adminhtml\Rewrite\Product;

    class Save extends \Magento\Catalog\Controller\Adminhtml\Product\save
    {

        public function execute()
        {
            echo "hello"; print_r($_POST); die;

            return parent::execute();
        }
    }

Jetzt in der executeFunktion erwarte ich POST-Wert von my_new_field. Aber ich verstehe es nicht. Nachdem ich das bekommen habe, werde ich benutzerdefinierte Abfragen verwenden, um Daten in einer benutzerdefinierten Tabelle zu speichern.

Was mache ich falsch oder sollte ich eine andere Methode verwenden?

Update: 26. August

Ich habe das Ajax-Formular verwendet, um Daten von der Produktregisterkarte zu speichern, da ich zeitliche Einschränkungen hatte. Ich habe die Antwort von @ william-oakley akzeptiert. Nun, wie @mageworx in seiner Antwort hinzufügte, ist dies kein Standardweg, um dies zu tun.

Ich möchte die UI-Formularstandardverwendung in der Weiterentwicklung verwenden. Meine Frage ist also, wie man der Produktbearbeitung mithilfe des UI-Formularstandards eine benutzerdefinierte Registerkarte hinzufügt und benutzerdefinierte Felder in einer benutzerdefinierten Tabelle oder auf andere Weise speichert.

HungryDB
quelle
1
Hey, Sie können die Datei CatalogProductSaveBefore.php in Observer für dieses benutzerdefinierte Speicherfeld verwenden.
Payal Patel

Antworten:

14

Sie können einfach ein "nacktes" Eingabefeld verwenden, Sie müssen nur das folgende Attribut hinzufügen:

data-form-part="product_form"

so:

<input data-form-part="product_form" type="text" name="my_new_field" value="123">

Sie können dann die POST-Daten für Ihre Eingabe abrufen.

William Oakley
quelle
7

Die obige Lösung ist nicht vollständig korrekt. Sie fügen ein Feld als "nacktes" HTML-Element hinzu, und ein Produktformular ist ein UI-Formular mit eigenen Besonderheiten. Eine spezielle Klasse ( vendor/magento/module-ui/view/base/web/js/form/form.js) ist für das Sammeln von Feldern und deren Validierung beim Senden eines Formulars verantwortlich. Außerdem sollte diese Klasse die Felder übersehen, die nicht mit diesem UI-Formular zusammenhängen oder nicht additional fieldswie alle Ihre Felder sind. Sie sollten die folgende Benennung verwenden, um sicherzustellen, dass Ihr Feld an die Steuerung gesendet wird:

input type="text" name="product[my_new_field]" value="123"

Dies ist jedoch nicht vollständig korrekt, da die richtige Lösung darin besteht, nicht von den Nutzungsstandards für Benutzeroberflächenformulare abzuweichen und deren native Elemente und Komponenten zu verwenden. In diesem Fall sollten Sie sich über so etwas keine Sorgen machen, da alles automatisch verarbeitet wird.

Sie können die Hauptmethode zum Speichern der UI-Formulardaten überprüfen, um den Prozess zu verstehen:

/**
 * Submits form
 *
 * @param {String} redirect
 */
submit: function (redirect) {
    var additional = collectData(this.additionalFields),
        source = this.source;

    _.each(additional, function (value, name) {
        source.set('data.' + name, value);
    });

    source.save({
        redirect: redirect,
        ajaxSave: this.ajaxSave,
        ajaxSaveType: this.ajaxSaveType,
        response: {
            data: this.responseData,
            status: this.responseStatus
        },
        attributes: {
            id: this.namespace
        }
    });
},

Wie Sie diesem Code entnehmen können, wird ein HTML-Formular mit all seinen Feldern nicht gesendet. Allerdings this.sourceund this.additionalFieldssind senden , aber Ihr Element ist nicht darin enthalten , weil sie falsch deklariert wird.

UPDATE VOM 23.08.2016

Hier ist das Beispiel zum Hinzufügen eines Feldsatzes aus unserem Blog. Sie können den vollständigen Artikel über den folgenden Link lesen:

Quelle: Eine einfache Möglichkeit, dem UI-Formular ein Feldset mit Feldern hinzuzufügen :

Fügen Sie den Inhalt hinzu: die Metadaten des UI-Formulars und den virtuellen Typ für das Hinzufügen.

Erstellen Sie eine Datei app/code/Vendor/Product/etc/adminhtml/di.xml. Wir werden einen Modifikator darin platzieren:

<?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="custom-fieldset" xsi:type="array">
                    <item name="class" xsi:type="string">Vendor\Product\Ui\DataProvider\Product\Form\Modifier\CustomFieldset</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                </item>
            </argument>
        </arguments>
    </virtualType>
</config>

Erstellen Sie nun die Modifikatordatei ( app/code/Vendor/Product/Ui/DataProvider/Product/Form/Modifier/CustomFieldset.php) mit einem benutzerdefinierten Feldsatz für die Produktbearbeitungsseite und füllen Sie sie mit den folgenden Feldern:

<?php
namespace Vendor\Product\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Framework\UrlInterface;
use Magento\Ui\Component\Container;
use Magento\Ui\Component\Form\Fieldset;
use Magento\Ui\Component\Form\Element\DataType\Number;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Element\Input;
use Magento\Ui\Component\Form\Element\Select;
use Magento\Ui\Component\Form\Element\MultiSelect;
use Magento\Ui\Component\Form\Field;

class CustomFieldset extends AbstractModifier
{

    // Components indexes
    const CUSTOM_FIELDSET_INDEX = 'custom_fieldset';
    const CUSTOM_FIELDSET_CONTENT = 'custom_fieldset_content';
    const CONTAINER_HEADER_NAME = 'custom_fieldset_content_header';

    // Fields names
    const FIELD_NAME_TEXT = 'example_text_field';
    const FIELD_NAME_SELECT = 'example_select_field';
    const FIELD_NAME_MULTISELECT = 'example_multiselect_field';

    /**
     * @var \Magento\Catalog\Model\Locator\LocatorInterface
     */
    protected $locator;

    /**
     * @var ArrayManager
     */
    protected $arrayManager;

    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * @var array
     */
    protected $meta = [];

    /**
     * @param LocatorInterface $locator
     * @param ArrayManager $arrayManager
     * @param UrlInterface $urlBuilder
     */
    public function __construct(
        LocatorInterface $locator,
        ArrayManager $arrayManager,
        UrlInterface $urlBuilder
    ) {
        $this->locator = $locator;
        $this->arrayManager = $arrayManager;
        $this->urlBuilder = $urlBuilder;
    }

    /**
     * Data modifier, does nothing in our example.
     *
     * @param array $data
     * @return array
     */
    public function modifyData(array $data)
    {
        return $data;
    }

    /**
     * Meta-data modifier: adds ours fieldset
     *
     * @param array $meta
     * @return array
     */
    public function modifyMeta(array $meta)
    {
        $this->meta = $meta;
        $this->addCustomFieldset();

        return $this->meta;
    }

    /**
     * Merge existing meta-data with our meta-data (do not overwrite it!)
     *
     * @return void
     */
    protected function addCustomFieldset()
    {
        $this->meta = array_merge_recursive(
            $this->meta,
            [
                static::CUSTOM_FIELDSET_INDEX => $this->getFieldsetConfig(),
            ]
        );
    }

    /**
     * Declare ours fieldset config
     *
     * @return array
     */
    protected function getFieldsetConfig()
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Fieldset Title'),
                        'componentType' => Fieldset::NAME,
                        'dataScope' => static::DATA_SCOPE_PRODUCT, // save data in the product data
                        'provider' => static::DATA_SCOPE_PRODUCT . '_data_source',
                        'ns' => static::FORM_NAME,
                        'collapsible' => true,
                        'sortOrder' => 10,
                        'opened' => true,
                    ],
                ],
            ],
            'children' => [
                static::CONTAINER_HEADER_NAME => $this->getHeaderContainerConfig(10),
                static::FIELD_NAME_TEXT => $this->getTextFieldConfig(20),
                static::FIELD_NAME_SELECT => $this->getSelectFieldConfig(30),
                static::FIELD_NAME_MULTISELECT => $this->getMultiSelectFieldConfig(40),
            ],
        ];
    }

    /**
     * Get config for header container
     *
     * @param int $sortOrder
     * @return array
     */
    protected function getHeaderContainerConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => null,
                        'formElement' => Container::NAME,
                        'componentType' => Container::NAME,
                        'template' => 'ui/form/components/complex',
                        'sortOrder' => $sortOrder,
                        'content' => __('You can write any text here'),
                    ],
                ],
            ],
            'children' => [],
        ];
    }

    /**
     * Example text field config
     *
     * @param $sortOrder
     * @return array
     */
    protected function getTextFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Example Text Field'),
                        'formElement' => Field::NAME,
                        'componentType' => Input::NAME,
                        'dataScope' => static::FIELD_NAME_TEXT,
                        'dataType' => Number::NAME,
                        'sortOrder' => $sortOrder,
                    ],
                ],
            ],
        ];
    }

    /**
     * Example select field config
     *
     * @param $sortOrder
     * @return array
     */
    protected function getSelectFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Options Select'),
                        'componentType' => Field::NAME,
                        'formElement' => Select::NAME,
                        'dataScope' => static::FIELD_NAME_SELECT,
                        'dataType' => Text::NAME,
                        'sortOrder' => $sortOrder,
                        'options' => $this->_getOptions(),
                        'visible' => true,
                        'disabled' => false,
                    ],
                ],
            ],
        ];
    }

    /**
     * Example multi-select field config
     *
     * @param $sortOrder
     * @return array
     */
    protected function getMultiSelectFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Options Multiselect'),
                        'componentType' => Field::NAME,
                        'formElement' => MultiSelect::NAME,
                        'dataScope' => static::FIELD_NAME_MULTISELECT,
                        'dataType' => Text::NAME,
                        'sortOrder' => $sortOrder,
                        'options' => $this->_getOptions(),
                        'visible' => true,
                        'disabled' => false,
                    ],
                ],
            ],
        ];
    }

    /**
     * Get example options as an option array:
     *      [
     *          label => string,
     *          value => option_id
     *      ]
     *
     * @return array
     */
    protected function _getOptions()
    {
        $options = [
            1 => [
                'label' => __('Option 1'),
                'value' => 1
            ],
            2 => [
                'label' => __('Option 2'),
                'value' => 2
            ],
            3 => [
                'label' => __('Option 3'),
                'value' => 3
            ],
        ];

        return $options;
    }
}

Vorschau

Das Speichern der Daten erfolgt in der Produktcontroller-Datei vendor/magento/module-catalog/Controller/Adminhtml/Product/Save.php in der Hauptausführungsmethode. Wenn alles richtig gemacht wurde, werden unsere Daten in den Eingabedaten dieser Methode korrekt angezeigt:

Vorschau

Hinweis: Wenn Ihr Produkt diese Attribute von Anfang an nicht hat, sollten Sie sie manuell speichern. Sie können dies im Beobachter tun.

app/code/Vendor/Product/etc/adminhtml/events.xmlDeklarieren Sie es zunächst in der Datei (wir verwenden den Bereich adminhtml, da das Formular im Front-End nicht vorhanden ist):

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_after">
        <observer name="save_example_data" instance="Vendor\Product\Observer\ProductSaveAfter" />
    </event>
</config>

Erstellen Sie dann die Beobachterklasse, auf die wir im Instanzattribut hingewiesen haben app/code/Vendor/Product/Observer/ProductSaveAfter.php:

<?php
namespace Vendor\Product\Observer;

use \Magento\Framework\Event\ObserverInterface;
use \Magento\Framework\Event\Observer as EventObserver;
use Vendor\Product\Ui\DataProvider\Product\Form\Modifier\CustomFieldset;

class ProductSaveAfter implements ObserverInterface
{

    /**
     * @param EventObserver $observer
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        /** @var \Magento\Catalog\Model\Product $product */
        $product = $observer->getEvent()->getProduct();
        if (!$product) {
            return;
        }

        $exampleTextField = $product->getData(CustomFieldset::FIELD_NAME_TEXT);
        $exampleSelectField = $product->getData(CustomFieldset::FIELD_NAME_SELECT);
        $exampleMultiSelectField = $product->getData(CustomFieldset::FIELD_NAME_MULTISELECT);

        // Manipulate data here
    }
}

Die Daten im Beobachter:

Vorschau

Jetzt können Sie vom Beobachter aus Ihr eigenes Modell aufrufen und darin Daten speichern oder nach Ihren Wünschen ändern.

Achtung! Wenn das Speichern Ihres Modells mit dem Speichern des Produkts verbunden ist, kann dies zur Rekursion führen.

MageWorx
quelle
Können Sie vorschlagen, wie ich UI-Formularfelder hinzufügen kann?
HungryDB
1
@HungryDB Wir haben die Antwort oben aktualisiert und den Link zum Artikel aus unserem Blog hinzugefügt. Dort können Sie lesen, wie Sie ein Feldset erstellen.
MageWorx
3
Danke für die Antwort @mageworx. Ich habe mich für die Ajax-Formularmethode entschieden, um Daten zu speichern, da ich zeitliche Einschränkungen habe. Ich werde auf jeden Fall Ihre Methode ausprobieren, wenn ich Zeit habe.
HungryDB
Wie speichere ich diese Daten in der Datenbank?
Chi
Danke für die Antwort. Diese Methode funktioniert. Ich habe ein benutzerdefiniertes Auswahlfeld hinzugefügt und der Wert wird mithilfe von Observer in meiner Tabelle gespeichert. Beim Bearbeiten desselben Produkts werden meine Werte jedoch nicht als ausgewählt angezeigt. Bitte helfen Sie.
Vindhuja
2

Um das Produktfeld in einer benutzerdefinierten Tabelle zu speichern, können Sie der Logik des Stufenpreises folgen. Magento spart den Tier-Preis mithilfe eines benutzerdefinierten Backend-Modells des Tier-Preises. Wir können dieselbe Logik für unser benutzerdefiniertes Feld / Attibute befolgen. Um attibute in einer benutzerdefinierten Tabelle zu speichern, müssen Sie das benutzerdefinierte Attribut erstellen und das Backend-Modell bereitstellen. Das Backend-Modell validiert und speichert den Attibute und ruft ihn ab. Sie können die folgenden Schritte ausführen.

Schritt 1. Produktattribut erstellen

<?php 
namespace Magentoins\TestAttribute\Setup; 
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;


class InstallData implements InstallDataInterface

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

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        /** @var EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

        /**
         * Add attributes to the eav/attribute
         */

        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'test_attribute',
            [
                'type' => 'int',
                'backend' => 'Magentoins\TestAttribute\Model\Product\Attribute\Backend\TestAttribute',
                'frontend' => '',
                'label' => 'Test Attribute',
                'input' => '',
                'class' => '',
                'source' => '',
                'global' => \Magento\Catalog\Model\Resource\Eav\Attribute::SCOPE_GLOBAL,
                'visible' => true,
                'required' => false,
                'user_defined' => false,
                'default' => 0,
                'searchable' => false,
                'filterable' => false,
                'comparable' => false,
                'visible_on_front' => false,
                'used_in_product_listing' => true,
                'unique' => false,
                'apply_to' => ''
            ]
        );
    }
}

Schritt 2. Erstellen Sie ein Backend-Modell für ein benutzerdefiniertes Produktattribut, das bei der Validierung hilft und den Attributwert speichert und abruft

<?php
namespace Magentoins\TestAttribute\Model\Product\Attribute\Backend;

class TestAttribute extends \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice
{
  protected $_productAttributeBackendTestAttribute;
  /**
   * Website currency codes and rates
   *
   * @var array
   */
  protected $_rates;

  protected $_helper;

  protected $eavConfig;

  public function __construct(
      \Magento\Directory\Model\CurrencyFactory $currencyFactory,
      \Magento\Store\Model\StoreManagerInterface $storeManager,
      \Magento\Catalog\Helper\Data $catalogData,
      \Magento\Framework\App\Config\ScopeConfigInterface $config,
      \Magento\Framework\Locale\FormatInterface $localeFormat,
      \Magento\Catalog\Model\Product\Type $catalogProductType,
      \Magento\Customer\Api\GroupManagementInterface $groupManagement,
      \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $productAttributeTierprice,
      \Magentoins\TestAttribute\Model\ResourceModel\Product\Attribute\Backend\TestAttribute $productAttributeBackendFixedprices,
      \Magentoins\TestAttribute\Helper\Data $helperData,
      \Magento\Eav\Model\Config $eavConfig
  ) {
    parent::__construct(
        $currencyFactory,
        $storeManager,
        $catalogData,
        $config,
        $localeFormat,
        $catalogProductType,
        $groupManagement,
        $productAttributeTierprice
    );
    $this->_productAttributeBackendTestAttribute = $productAttributeBackendTestAttribute;    

  }

  /**
   * Retrieve resource instance
   *
   */
  protected function _getResource()
  {
    return $this->_productAttributeBackendTestAttribute;
  }

  public function getAttribute()
  {
    $attribute = $this->eavConfig->getAttribute('catalog_product', 'test_attribute');
    return $attribute;
  }
  /**
   * Validate test_attribute data
   *
   */
  public function validate ($object)
  {
    $attribute = $this->getAttribute();
    $attr = $object->getData($attribute->getName());
    if (empty($attr)) {
      return true;
    }    

    return true;
  }

  /**
   * Assign test_attribute to product data   
   */
  public function afterLoad ($object)
  {
    /*$data is from your custom table*/
    $data = $this->_getResource()->loadTestAttributeData($object->getId(), $websiteId);
    $object->setData($this->getAttribute()->getName(), $data);
    $object->setOrigData($this->getAttribute()->getName(), $data);

    $valueChangedKey = $this->getAttribute()->getName() . '_changed';
    $object->setOrigData($valueChangedKey, 0);
    $object->setData($valueChangedKey, 0);

    return $this;
  }

  /**
   * After Save Attribute manipulation 
   */
  public function afterSave ($object)
  {
    $websiteId = $this->_storeManager->getStore($object->getStoreId())->getWebsiteId();
    $isGlobal = $this->getAttribute()->isScopeGlobal() || $websiteId == 0;

    $testAttribute = $object->getData($this->getAttribute()->getName());

    /*Save attribute value in custom table with the help of resource model*/

    $this->_getResource()->saveTestAttributeData($testAttribute);

    return $this;
  }

  public function beforeSave ($object)
  {
    parent::beforeSave($object);        
  }

}

Schritt 2. Ressourcenmodell zum Speichern und Abrufen des Attributwerts aus einer benutzerdefinierten Tabelle

<?php
namespace Magentoins\TestAttribute\Model\ResourceModel\Product\Attribute\Backend;

use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice;

/**
 * @author
 */
class TestAttribute extends Tierprice
{
    /**
     * Initialize connection and define main table
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('magentoins_product_entity_testAttribute', 'value_id');
    }

    /**
     * Load Fixed Prices for product
     *
     * @param int $productId
     * @return Designnbuy_Fixedprices_Model_Mysql4_fixedprices
     */
    public function loadTestAttributeData($productId, $websiteId = null)
    {
        $connection = $this->getConnection();
        $columns = array (
            'test_attribute' => $this->getIdFieldName()            
        );
        $select = $connection->select()
            ->from($this->getMainTable(), $columns)
            ->where('entity_id=?', $productId)
            ->order('order');

        if (!is_null($websiteId)) {
            if ($websiteId == '0') {
                $select->where('website_id=?', $websiteId);
            } else {
                $select->where('website_id IN(?)', array ('0', $websiteId
                ));
            }
        }

        return $connection->fetchAll($select);
    }

    public function saveTestAttributeData(\Magento\Framework\DataObject $attributeObject)
    {
        $connection = $this->getConnection();
        $data = $this->_prepareDataForTable($attributeObject, $this->getMainTable());

        if (!empty($data[$this->getIdFieldName()])) {
            $where = $connection->quoteInto($this->getIdFieldName() . ' = ?', $data[$this->getIdFieldName()]);
            unset($data[$this->getIdFieldName()]);
            $connection->update($this->getMainTable(), $data, $where);
        } else {
            $connection->insert($this->getMainTable(), $data);
        }
        return $this;
    }
}
Ajay
quelle