Die beste Möglichkeit, ein dynamisches Raster als Eingabe in Adminhtml für benutzerdefinierte Erweiterungen hinzuzufügen

15

Ich habe eine benutzerdefinierte Filialfinder-Erweiterung mit einem eigenen Raster erstellt und Seiten im Adminhtml bearbeitet, und alles funktioniert hervorragend. Für die Öffnungszeiten der Filialen möchte ich ein dynamisches Raster wie für die Attributoptionen implementieren.

Bildbeschreibung hier eingeben

Jetzt habe ich eine Lösung gefunden, aber ich hoffe, dass es einen besseren oder zumindest saubereren Weg gibt. Bisher habe ich dem Feld im Formular einen Renderer hinzugefügtfieldset

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('rkstorelocator_form', array('legend'=>Mage::helper('rkstorelocator')->__('Store information')));

        [...]

        $officehours_field = $fieldset->addField('office_hours', 'editor', array(
            'name'      => 'office_hours',
            'label'     => Mage::helper('rkstorelocator')->__('Office hours'),
            'required'  => false,
        ));

        $officehours_block = $this->getLayout()
                                ->createBlock('rkstorelocator/adminhtml_rkstorelocator_edit_renderer_officehours')
                                ->setData(array(
                                    'name'      => 'office_hours',
                                    'label'     => Mage::helper('rkstorelocator')->__('Office hours'),
                                    'required'  => false,
                                ));

        $officehours_field->setRenderer($officehours_block);

        [...]
    }
}

Und eine Blockklasse zum Rendern

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Renderer_Officehours
 extends Mage_Adminhtml_Block_Abstract 
 implements Varien_Data_Form_Element_Renderer_Interface 
{

    public function render(Varien_Data_Form_Element_Abstract $element) 
    {
        $required_indicator = $this->getData('required') ? '<span class="required">*</span>' : '' ;

        $html = '
<table id="attribute-options-table" class="dynamic-grid rkstorelocator-officehours" cellspacing="0" cellpadding="0"><tbody>
    <tr>
        <th>Day indicator</th>
        <th>Opening hour</th>
        <th>Closing hour</th>
        <th>
            <button id="add_new_option_button" title="Add Option" type="button" class="scalable add"><span><span><span>Add Option</span></span></span></button>
        </th>
    </tr>
</tbody></table>

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

var _form_html_row = \'<tr class="option-row rkstorelocator-officehours-dayrow" id="hour-row-{{id}}"><td><input name="'.$this->getData('name').'[value][option_{{id}}][0]" value="" class="input-text required-option" type="text"></td><td><input name="'.$this->getData('name').'[value][option_{{id}}][2]" value="" class="input-text required-option" type="text"></td><td><input name="'.$this->getData('name').'[value][option_{{id}}][2]" value="" class="input-text required-option" type="text"></td><td class="a-left" id="delete_button_container_option_{{id}}"><input type="hidden" class="delete-flag" name="'.$this->getData('name').'[delete][option_{{id}}]" value=""/><button onclick="$(\\\'hour-row-{{id}}\\\').remove();" title="Delete" type="button" class="scalable delete delete-option"><span><span><span>Delete</span></span></span></button></td></tr>\';

var _rkstorelocator_counter = 0;

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

//]]></script>
        ';
        return $html;
    }

}

Welches gibt mir das folgende Ergebnis Bildbeschreibung hier eingeben

Das funktioniert im Grunde genommen, aber die aktuellen Werte zu ermitteln, wird ziemlich chaotisch sein, und insgesamt bin ich nicht allzu stolz auf den Code, den ich geschrieben habe (wie Sie sich vorstellen können).

Ich habe nach mehreren Lösungen gegoogelt, aber im Allgemeinen verfolgen alle diesen Ansatz. Kennt jemand einen saubereren Weg, um dies zu tun?

Sander Mangel
quelle
Kann ich den vollständigen Code jeder für dieses benutzerdefinierte Modul verwendeten Datei haben?
UdayPavan Reddy

Antworten:

10

Es hat viel zu lange gedauert, bis mir klar wurde, dass die Tier-Preise so aussehen, wie ich es will. Nachdem ich mir angesehen hatte, wie Magento mit Tier Pricing umgeht, ging ich folgendermaßen vor. Entschuldigung im Voraus für die riesigen Codeblöcke, aber ich dachte, es könnte für zukünftige Referenz interessant sein.

In meiner Formklasse Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Tab_General

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('rkstorelocator_form', array('legend'=>Mage::helper('rkstorelocator')->__('Store information')));

        [...]

        $officehours_field = $fieldset->addField('office_hours', 'text', array(
            'name'      => 'office_hours',
            'label'     => Mage::helper('rkstorelocator')->__('Office hours'),
            'required'  => false,
        ));

        $office_hours = $form->getElement('office_hours');

        $office_hours->setRenderer(
            $this->getLayout()->createBlock('rkstorelocator/adminhtml_rkstorelocator_edit_renderer_officehours')
        );


        [...]
    }
}

Nun zur Sprechstunde Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Renderer_Officehours.

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Renderer_Officehours
 extends Mage_Adminhtml_Block_Widget
 implements Varien_Data_Form_Element_Renderer_Interface
{

    /**
     * Initialize block
     */
    public function __construct()
    {
        $this->setTemplate('rkstorelocator/officehours.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();
    }

}

Und die Vorlage .phtml-Datei adminhtml/default/default/template/rkstorelocator/officehours.phtml

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

$collection = Mage::registry('rkstorelocator_data')
                ->getOpeningHours()
                ->setOrder('sortorder', 'ASC');

$_counter = 0;
?>
<tr>
    <td class="label"><?php echo $this->getElement()->getLabel() ?></td>
    <td colspan="10" class="grid hours">
        <table id="attribute-options-table" class="dynamic-grid rkstorelocator-officehours" cellspacing="0" cellpadding="0"><tbody>
            <tr>
                <th><?php echo $this->__('Day label') ?></th><th><?php echo $this->__('Opening hour') ?></th><th><?php echo $this->__('Closing hour') ?></th><th><?php echo $this->__('Sortorder') ?></th>
                <th><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 rkstorelocator-officehours-dayrow" id="hour-row-<?php echo $_counter?>">
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][dayindicator]" value="<?php echo $_item->getDayindicator() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][openinghour]" value="<?php echo $_item->getOpeninghour() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][closinghour]" value="<?php echo $_item->getClosinghour() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][sortorder]" value="<?php echo $_item->getSortorder() ?>" 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 rkstorelocator-officehours-dayrow" id="hour-row-{{id}}"><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][dayindicator]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][openinghour]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][closinghour]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][sortorder]" 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="$(\'hour-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 _rkstorelocator_counter = <?php echo $_counter?>;

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

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

Und das Ergebnis: Bildbeschreibung hier eingeben

Sehr geehrte zukünftige Googler, wenn Sie dies lesen, wird Magento 2.x veröffentlicht. Hoffen wir, dass Magento solche Dinge ein bisschen einfacher gemacht hat. :)

Sander Mangel
quelle
Wie speichert man sie in der DB? Als serialisierte Zeichenfolge?
Sergei Guk
Nein, ich habe einen zweiten Tisch für die Sprechstunde. Besser als serialisieren
Sander Mangel
vereinbart, müssen nur das gleiche für Produkte hinzufügen und es wird etwa 10 Name-Wert-Paar geben. Überlegen Sie sich also, ob Sie sie als serialisierte Zeichenfolgen speichern oder Tabellen mit 1 Paar pro Zeile pro Produkt erstellen möchten. Trotzdem danke für den Beitrag!
Sergei Guk
Der Grund, warum ich keine serialisierten Daten verwenden wollte, ist, dass Sie in der Datenbank nichts damit anfangen können. In der nächsten Version dieser Erweiterung möchte ich Standorte basierend auf Öffnungszeiten suchen. Mit serialisierten Daten wäre das unmöglich. Jetzt habe ich ein Sammlungsobjekt, das ich verwenden kann. Die zusätzlichen Daten, die durch die zweite Tabelle verursacht wurden, waren ein kleiner Kompromiss
Sander Mangel
Rettete meinen Tag! Dank dafür. Ich nehme an, $ this-> setTemplate ('rkstorelocator / officehours.phtml'); war der Trick. Ich wünschte, es gäbe einen besseren Ansatz für Javascript-Bit.
Xelber
3

Ich gebe einige meiner Codes, die auf Magento-Vorlagen basieren. Vielleicht wird es nützlich sein.
Einige Registerkarten:

<?php
class Ssd_Shower_Block_Adminhtml_Shower_Edit_Tab_Options
    extends Mage_Adminhtml_Block_Template
    implements Mage_Adminhtml_Block_Widget_Tab_Interface
{

    /** set own teplate */
    public function __construct()
    {
        $this->setTemplate('pregnancy/list/options.phtml');
    }

    /** here some implementation of tab interfeys */


    /** options for every row, they will be rendered as dynamic row with inputs */
    public function getOptionValues()
    {
        $period=$this->getData('period');
        $optionsArr = Mage::helper('shower')->getTipList($period);

        $values = array();
        foreach ($optionsArr as $option) {

            $value = array();
            $value['id'] = $option->getId();
            $value['period_id'] = $period->getId();
            $value['tip_content'] = $option->getTip_content();
            $value['sort_order'] = $option->getSort_order();
            $value['update'] = 1;

            $values[] = new Varien_Object($value);
        }

        return $values;
    }

}

?>

Und Schwangerschaft / Liste / options.phtml Vorlage:

<div class="entity-edit" id="manage-options-panel">
    <div class="entry-edit-head">
        <h4 class="icon-head head-edit-form fieldset-legend">Some title</h4>
    </div>
    <div class="box">
        <div class="hor-scroll">
            <table class="dynamic-grid" cellspacing="0" cellpadding="0" width="100%">
                <tr id="grid_head">
                    <th style="width:90%!important"><?php echo Mage::helper('pregnancy')->__('Checklist Items') ?></th>
                    <th class="w-150"><?php echo Mage::helper('pregnancy')->__('Position') ?></th>
                    <th class="w-150">
                        <button id="add_new_option_button" class="scalable add" style="" onclick="" type="button">
                            <span><?php echo Mage::helper('pregnancy')->__('Add Checklist Item') ?></span>
                        </button>
                    </th>
                </tr>
                <tr id="attribute-options-table">
                </tr>
                <tr class="no-display template" id="row-template">
                    <td><input name="tip[{{id}}][tip_content]"
                               value="{{tip_content}}"
                               class="input-text required-option full"
                               type="text" disabled="disabled"/></td>

                    <td class="a-center"><input class="input-text" type="text" name="tip[{{id}}][sort_order]"
                                                value="{{sort_order}}"/></td>
                    <td class="a-left">
                        <input type="hidden" class="delete-flag" name="tip[{{id}}][delete]" value=""/>
                        <input type="hidden" class="update-flag" name="tip[{{id}}][update]" value="{{update}}"/>
                        <button class="scalable delete delete-option" type="button"><span>Delete</span></button>
                    </td>
                </tr>
            </table>
        </div>
        <input type="hidden" id="option-count-check" value=""/>
    </div>
</div>

<script type="text/javascript">
    //<![CDATA[
    var optionDefaultInputType = 'text';
    //template for dynamic row
    var templateText =
            '<tr class="option-row">' +
                    '<td><input name="tip[{{id}}][tip_content]" value="{{tip_content}}" class="input-text required-option full" type="text"/><\/td>' +
                    '<td><input class="input-text" type="text" name="tip[{{id}}][sort_order]" value="{{sort_order}}"/><\/td>' +
                    '<td class="a-left">' +
                    '<input type="hidden" class="delete-flag" name="tip[{{id}}][delete]" value="" />' +
                    '<input type="hidden" class="update-flag" name="tip[{{id}}][update]" value="{{update}}"/>' +
                    '<button class="scalable delete delete-option" type="button"><span><?=$this->__("Delete")?></span></button>' +
                    '<\/td>' +
                    '<\/tr>';

    var attributeOption = {
        table : $('attribute-options-table'),
        templateSyntax : /(^|.|\r|\n)({{(\w+)}})/,
        templateText : templateText,
        itemCount : 0,
        totalItems : 0,
        //add dynamic row function
        add : function(data) {
            this.template = new Template(this.templateText, this.templateSyntax);
            if (!data.id) {
                data = {};
                data.id = 'option_' + this.itemCount;
            }
            if (!data.intype)
                data.intype = optionDefaultInputType;

            Element.insert(this.table, {before: this.template.evaluate(data)});
            this.bindRemoveButtons();
            this.itemCount++;
            this.totalItems++;
            this.updateItemsCountField();
        },
        //remove dynamic row function
        remove : function(event) {
            if (confirm('<?php echo $this->__("Do you really delete this tip?");?>')) {
                var element = $(Event.findElement(event, 'tr'));
                element.ancestors().each(function(parentItem) {
                    if (parentItem.hasClassName('option-row')) {
                        element = parentItem;
                        throw $break;
                    } else if (parentItem.hasClassName('box')) {
                        throw $break;
                    }
                });

                if (element) {
                    var elementFlags = element.getElementsByClassName('delete-flag');
                    if (elementFlags[0]) {
                        elementFlags[0].value = 1;
                    }

                    element.addClassName('no-display');
                    element.addClassName('template');
                    element.hide();
                    this.totalItems--;
                    this.updateItemsCountField();
                }
            }
        },
        updateItemsCountField: function() {
            if (this.totalItems > 0) {
                $('option-count-check').value = '1';
            } else {
                $('option-count-check').value = '';
            }
        },
        bindRemoveButtons : function() {
            var buttons = $$('.delete-option');
            for (var i = 0; i < buttons.length; i++) {
                if (!$(buttons[i]).binded) {
                    $(buttons[i]).binded = true;
                    Event.observe(buttons[i], 'click', this.remove.bind(this));
                }
            }
        }

    }
    if ($('row-template')) {
        $('row-template').remove();
    }
    attributeOption.bindRemoveButtons();

    if ($('add_new_option_button')) {
        Event.observe('add_new_option_button', 'click', attributeOption.add.bind(attributeOption));
    }
    Validation.addAllThese([
        ['required-option', '<?php echo Mage::helper('pregnancy')->__('Failed') ?>', function(v) {
            return !Validation.get('IsEmpty').test(v);
        }]
    ]);
    Validation.addAllThese([
        ['required-options-count', '<?php echo Mage::helper('pregnancy')->__('Options is required') ?>', function(v) {
            return !Validation.get('IsEmpty').test(v);
        }]
    ]);
<?php
    /** pulling data from Ssd_Shower_Block_Adminhtml_Shower_Edit_Tab_Options **/
    if ($options = $this->getOptionValues()) {
        foreach ($options as $_value): ?>
        attributeOption.add(<?php echo $_value->toJson() ?>);
    <?php endforeach; } ?>
    //]]>
</script>
mageUz
quelle
Danke für die Antwort, es entspricht in gewisser Weise der Art und Weise, wie ich es gelöst habe. Es gibt ein paar Teile Ihres HTML-Codes, die ich gerne verwenden würde :-)
Sander Mangel