Как вставить сетку с несколькими столбцами в форму администратора?


Мне нужно вставить сетку в форму администратора, содержащую два столбца, и в каждой строке должна быть кнопка удаления. Есть ли способ достичь этого enter image description here

Author: Fabian Schmengler, 2015-03-13

2 answers

Тебе нужна пара вещей. Прежде всего, рядом с обычным table, который вы создадите для своего модуля, в котором хранятся данные формы adminhtml, вам понадобится второй table, в котором хранятся данные URL-адресов с полями

  1. идентификатор сущности
  2. идентификатор родителя
  3. метка
  4. url-адрес
  5. сортировщик

Создайте соответствующие модели ресурсов.

Рендеринг в Adminhtml

Добавление нового средства визуализации в генератор форм. Я только что добавил поле, в котором будет отображаться средство визуализации

app/code/[codepool]/Namespace/Module/BLock/Adminhtml/[Module]/Edit/Tab/General.php

class [Namespace]_[Module]_Block_Adminhtml_[Module]_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('[module]_form', array('legend'=>Mage::helper('[namespace]_[module]')->__('General')));

        [...]       

        $urlsField = $fieldset->addField('urls', 'text', array(
            'name'      => 'urls',
            'label'     => Mage::helper('[namespace]_[module]')->__('URLS'),
            'required'  => false,
        ));

    $urlsField = $form->getElement('urls');

    $urlsField->setRenderer(
        $this->getLayout()->createBlock('[namespace]_[module]/adminhtml_[module]_edit_renderer_urls')
    );

    [...]
    }
}

Класс визуализации, который добавляет шаблон phtml

app/code/[codepool]/Namespace/Module/BLock/Adminhtml/[Module]/Edit/Renderer/Urls.php

class [Namespace]_[Module]_Block_Adminhtml_[Module]_Edit_Renderer_Urls
 extends Mage_Adminhtml_Block_Widget
 implements Varien_Data_Form_Element_Renderer_Interface
{

    /**
     * Initialize block
     */
    public function __construct()
    {
        $this->setTemplate('[namespace]_[module]/urls.phtml');
    }

    /**
     * Render HTML
     *
     * @param Varien_Data_Form_Element_Abstract $element
     * @return string
     */
    public function render(Varien_Data_Form_Element_Abstract $element)
    {
        $this->setElement($element);
        return $this->toHtml();
    }

}

И шаблон, который отображает заполненные параметры и добавляет шаблон javascript для других строк.

app/design/adminhtml/default/default/template/[namespace]_[module]/urls.phtml

$_htmlId      = $this->getElement()->getHtmlId();
$_htmlClass   = $this->getElement()->getClass();
$_htmlName    = $this->getElement()->getName();
$_readonly    = $this->getElement()->getReadonly();

$collection = Mage::registry('[namespace]_[module]_data')->getUrls(); // this gets the URLs from the model that is loaded for this item, so the class [Namespace]_[Module]_Model_[Object] needs a method `getUrls`

$_counter = 0;
?>
<tr>
    <td class="label"><?php echo $this->getElement()->getLabel() ?></td>
    <td colspan="10" class="grid hours">
        <table id="attribute-options-table" class="data border [module]-urls" cellspacing="0" cellpadding="0"><tbody>
            <tr class="headings">
                <th><?php echo $this->__('Label') ?></th>
                <th><?php echo $this->__('Url') ?></th>
                <th class="last"><button id="add_new_option_button" title="Add Option" type="button" class="scalable add"><span><span><span><?php echo $this->__('Add Option') ?></span></span></span></button></th>
            </tr>
<?php foreach ($collection as $_item): ?>
<tr class="option-row [module]-urls-row" id="urls-row-<?php echo $_counter?>">
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][label]" value="<?php echo $_item->getLabel() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][url]" value="<?php echo $_item->getUrl() ?>" class="input-text" type="text"></td>
    <td class="a-left" id="delete_button_container_option_<?php echo $_counter ?>'">
        <input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][id]" value="<?php echo $_item->getId() ?>" type="hidden">
        <input id="delete-row-<?php echo $_counter ?>" type="hidden" class="delete-flag" name="<?php echo $_htmlName; ?>[delete][option_<?php echo $_counter ?>]" value=""/>
        <button onclick="$('hour-row-<?php echo $_counter ?>').style.display='none'; $('delete-row-<?php echo $_counter ?>').setValue(1);" title="Delete" type="button" class="scalable delete delete-option"><span><span><span>Delete</span></span></span></button>
    </td>
</tr>
<?php
        $_counter++;
    endforeach;
?>
</tbody></table>

<script type="text/javascript">//<![CDATA[

var _form_html_row = '<tr class="option-row [module]-urls-row" id="urls-row-{{id}}"><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][label]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][url]" value="" class="input-text" type="text"></td><td class="a-left" id="delete_button_container_option_{{id}}"><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][id]" value="" type="hidden"><input id="delete-row-{{id}}" type="hidden" class="delete-flag" name="<?php echo $_htmlName; ?>[delete][option_{{id}}]" value=""/><button onclick="$(\'urls-row-{{id}}\').style.display=\'none\'; $(\'delete-row-{{id}}\').setValue(1);" title="Delete" type="button" class="scalable delete delete-option"><span><span><span>Delete</span></span></span></button></td></tr>';

var _urls_counter = <?php echo $_counter?>;

$('add_new_option_button').observe('click', function(){
    $('attribute-options-table').insert(_form_html_row.replace(/\{\{id\}\}/ig, _urls_counter));
    _urls_counter++;
});

//]]></script>
    </td>
</tr>

Сохранение данных при сохранении

app/code/[codepool]/[Namespace]/[Module]/controllers/Adminhtml/[Module]Controller.php

class [Namespace]_[Module]_Adminhtml_[Module]Controller 
 extends Mage_Adminhtml_Controller_Action
{

    public function saveAction()
    {
        if ( $this->getRequest()->getPost() ) 
        {
            try {
                $postData = $this->getRequest()->getPost();

                // Save the data
                $model = Mage::getModel('[namespace]_[module]/object');

                $urls = $postData['urls'];
                unset($postData['urls']);
                $model->setData($postData);

                $model->save();


                $parentId = $model->getId();

                // save urls
                if (!empty($urls))
                {
                    foreach ($urls['delete'] as $_key => $_row)
                    {
                        $delete = (int)$_row;
                        $urlsData = $urls['value'][$_key];

                        $_urls = Mage::getModel('[namespace]_[module]/[urls]')->load((int)$urlsData['id']); // this is the model that stores the URLs data in a second table

                        if ($delete && 0 < (int)$_urls->getId()) // exists & required to delete
                        {
                            $_urls->delete();
                            continue;
                        }

                        if (!$delete)
                        {
                            if (0 == (int)$_urls->getId()) // new item
                            {
                                Mage::getModel('[namespace]_[module]/[urls]')->setData(array(
                                    'parent_id' => $parentId,
                                    'label' => $urlsData['label'],
                                    'url'   => $urlsData['url'],
                                    'sortorder' => $urlsData['sortorder'],
                                ))->save();
                            }
                            else
                            {
                                $_urls->addData(array(
                                    'store_id'  => $parentId,
                                    'label' => $urlsData['label'],
                                    'url'   => $urlsData['url'],
                                    'sortorder' => $urlsData['sortorder'],
                                ))->save();
                            }
                        }
                    }
                }

                // And wrap up the transaction
                Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Item was successfully saved'));
                [...]

                $this->_redirect('*/*/');
                return;
            } catch (Exception $e) {
                [...]
            }
        }
        $this->_redirect('*/*/');
    }

}

Теперь ранее я ссылался на метод getUrls в объектной модели. Это выглядело бы как-то вот так.

app/code/[codepool]/[Namespace]/[Module]/Model/[Object].php

class [Namespace]_[Module]_Model_[Object] extends Mage_Core_Model_Abstract
{

    public function getOpeningHours()
    {
        return Mage::getModel('[namespace]_[module]/urls')->getCollection()
                    ->addFieldToFilter('parent_id', $this->getId());
    }
}

И это все!

 4
Author: Sander Mangel, 2015-03-13 10:04:11

Все гораздо проще. Я описал это полностью на http://www.integer-net.com/2015/03/17/how-to-create-tables-in-magento-system-configuration/. Короче говоря:

System.xml :

<shipping_costs translate="label">
    <label>Shipping Cost based on Price</label>
    <frontend_model>namespace_module/config_shippingCosts</frontend_model>
    <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
    <sort_order>10</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
    <show_in_store>0</show_in_store>
</shipping_costs>

Как вы можете видеть, код добавляет только внешнюю модель и внутреннюю модель к обычному определению новых полей.

Внутренняя модель используется для изменения сохранения значения поля в базу данных и загрузки из нее. Всегда используйте adminhtml/system_config_backend_serialized_array для администратора здесь. Модель интерфейса на самом деле представляет собой блок, несмотря на свое название. Он используется для отображения таблицы с определенными столбцами. Интерфейсные модели всегда наследуются от класса Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract.

В моем примере класс модели интерфейса выглядит следующим образом:

<?php
class Namespace_Module_Block_Config_ShippingCosts 
    extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract
{
    public function _prepareToRender()
    {
        $this->addColumn('from_price', array(
            'label' => Mage::helper('namespace_module')->__('From Price'),
            'style' => 'width:100px',
        ));
        $this->addColumn('cost', array(
            'label' => Mage::helper('namespace_module')->__('Shipping Cost'),
            'style' => 'width:100px',
        ));

        $this->_addAfter = false;
        $this->_addButtonLabel = Mage::helper('namespace_module')->__('Add');
    }
}

Больше ничего не требуется для отображения таблицы с двумя столбцами по 100 пикселей каждый.

Если вы хотите где-то использовать значения из конфигурации, вам просто нужно отменить сериализацию данные поля:

$shippingCosts = Mage::getStoreConfig('namespace_module/general/shipping_costs');
if ($shippingCosts) {
    $shippingCosts = unserialize($shippingCosts);
    if (is_array($shippingCosts)) {
        foreach($shippingCosts as $shippingCostsRow) {
            $fromPrice = $shippingCostsRow['from_price'];
            $cost = $shippingCostsRow['cost'];
            ...
        }
    } else {
        // handle unserializing errors here
    }
}
 7
Author: Andreas von Studnitz, 2015-03-17 11:34:13