最新消息:Excel无乱码转CSV,由于工作原因很少更新博客和回复大家的评论,非常抱歉。

Magento 开发后台管理Grids和Forms

In my humble opinion, creating new sections in the Admin Panel are a tad bit more complicated than creating new features on the frontend. Hopefully, this post will help get you a few steps closer to being able to understand and create adminhtml grids and forms.

The first thing you need to do is create a menu item to get to your new grid, then you begin the exciting journey into some Adminhtml action.

The Config
How about I just throw the whole stinkin’ config.xml file out here right off the bat:

<config>
    <modules>
        <Super_Awesome>
            <version>0.1.0</version>
        </Super_Awesome>
    </modules>
    <adminhtml>
        <!-- The <layout> updates allow us to define our block layouts in a seperate file so are aren't messin' with the magento layout files.  -->
        <layout>
            <updates>
                <awesome>
                    <file>awesome.xml</file>
                </awesome>
            </updates>
        </layout>
        <!-- The <acl> section is for access control. Here we define the pieces where access can be controlled within a role. -->
        <acl>
            <resources>
                <admin>
                    <children>
                        <awesome>
                            <title>Awesome Menu Item</title>
                            <children>
                                <example translate="title" module="awesome">
                                    <title>Example Menu Item</title>
                                </example>
                            </children>
                        </awesome>
                    </children>
                </admin>
            </resources>
        </acl>
    </adminhtml>
    <admin>
        <!--
            Here we are telling the Magento router to look for the controllers in the Super_Awesome_controllers_Adminhtml before we look in the
            Mage_Adminhtml module for all urls that begin with /admin/controller_name
         -->
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <awesome before="Mage_Adminhtml">Super_Awesome_Adminhtml</awesome>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>

    <global>
        <models>
            <awesome>
                <class>Super_Awesome_Model</class>
                <resourceModel>awesome_mysql4</resourceModel>
            </awesome>
             <awesome_mysql4>
                <class>Super_Awesome_Model_Mysql4</class>
                <entities>
                    <example>
                        <table>Super_Awesome_example</table>
                    </example>
                </entities>
            </awesome_mysql4>
        </models>

        <resources>
            <awesome_setup>
                <setup>
                    <module>Super_Awesome</module>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </awesome_setup>
            <awesome_write>
                <connection>
                    <use>core_write</use>
                </connection>
            </awesome_write>
            <awesome_read>
                <connection>
                    <use>core_read</use>
                </connection>
            </awesome_read>
        </resources>

        <blocks>
            <awesome>
                <class>Super_Awesome_Block</class>
            </awesome>
        </blocks>
        <helpers>
            <awesome>
                <class>Super_Awesome_Helper</class>
            </awesome>
        </helpers>
    </global>

</config>

Some things of note about the config.xml:

  1. There is a layout file defined (awesome.xml)
  2. There is a change to the adminhtml router that tells the route to look in our module before looking into Mage_Adminhtml
  3. Everything else is pretty straight-forward (models, resource models, collections, setup, blocks, helpers…)

The Layout
Before I forget, here is the contents of the layout file (design/adminhtml/default/default/layout/awesome.xml):

<?xml version="1.0"?>

<layout>
    <adminhtml_example_index>
        <reference name="content">
            <block type="awesome/adminhtml_example" name="example" />
        </reference>
    </adminhtml_example_index>

     <adminhtml_example_edit>
        <reference name="content">
            <block type="awesome/adminhtml_example_edit" name="example_edit" />
        </reference>
    </adminhtml_example_edit>

</layout>

We don’t need to define much in here. The reason I think that Admin Panel coding is a little more complicated is because there is a lot of things that happen behind the scenes that are not driven by the layout as we normally see it.

Install Script
For testing/example purposes, I also created an install script to create a table and load up some data. In the sql/awesome_setup/mysql4-install-0.1.0.php file, I have:

<?php

$installer = $this;

$installer->startSetup();

$installer->run("

-- DROP TABLE IF EXISTS {$this->getTable('super_awesome_example')};
CREATE TABLE {$this->getTable('super_awesome_example')} (
  `id` int(11) unsigned NOT NULL auto_increment,
  `name` varchar(100) NOT NULL,
  `description` varchar(100) NOT NULL,
  `other` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

INSERT INTO {$this->getTable('super_awesome_example')} (name, description, other) values ('Example 1', 'Example One Description', 'This first example is reall awesome.');
INSERT INTO {$this->getTable('super_awesome_example')} (name, description, other) values ('Example 2', 'Example Two Description', 'This second example is reall awesome.');
INSERT INTO {$this->getTable('super_awesome_example')} (name, description, other) values ('Example 3', 'Example Three Description', 'This third example is reall awesome.');
INSERT INTO {$this->getTable('super_awesome_example')} (name, description, other) values ('Example 4', 'Example Four Description', 'This fourth example is reall awesome.');

");

$installer->endSetup()

The Models
In this example, I am going to assume you know how to create a model, its resource model, and its collection model. I created the following classes:

  • Super_Awesome_Model_Example
  • Super_Awesome_Model_Mysql4_Example
  • Super_Awesome_Model_Mysql4_Example_Collection

The Controller

    Hopefully, we all know what a controller does, so I won’t explain that part of the MVC pattern. The adminhtml controllers generally provide actions to do basic CRUD operations on the model. In ours, you will find the following actions:
  • index – Shows the grid.
  • edit – Shows the edit/new form.
  • save – Saves the form data.
  • delete – Deletes the model.
  • new – Forwards on to the edit action

There really isn’t anything crazy going on here, so I would just take a few minutes to read through the code and get an understanding of what each function does (and does not do):

<?php

class Super_Awesome_Adminhtml_ExampleController extends Mage_Adminhtml_Controller_Action
{

    public function indexAction()
    {

        $this->loadLayout();
        $this->renderLayout();
    }

    public function newAction()
    {
        $this->_forward('edit');
    }

    public function editAction()
    {
        $id = $this->getRequest()->getParam('id', null);
        $model = Mage::getModel('awesome/example');
        if ($id) {
            $model->load((int) $id);
            if ($model->getId()) {
                $data = Mage::getSingleton('adminhtml/session')->getFormData(true);
                if ($data) {
                    $model->setData($data)->setId($id);
                }
            } else {
                Mage::getSingleton('adminhtml/session')->addError(Mage::helper('awesome')->__('Example does not exist'));
                $this->_redirect('*/*/');
            }
        }
        Mage::register('example_data', $model);

        $this->loadLayout();
        $this->getLayout()->getBlock('head')->setCanLoadExtJs(true);
        $this->renderLayout();
    }

    public function saveAction()
    {
        if ($data = $this->getRequest()->getPost())
        {
            $model = Mage::getModel('awesome/example');
            $id = $this->getRequest()->getParam('id');
            if ($id) {
                $model->load($id);
            }
            $model->setData($data);

            Mage::getSingleton('adminhtml/session')->setFormData($data);
            try {
                if ($id) {
                    $model->setId($id);
                }
                $model->save();

                if (!$model->getId()) {
                    Mage::throwException(Mage::helper('awesome')->__('Error saving example'));
                }

                Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('awesome')->__('Example was successfully saved.'));
                Mage::getSingleton('adminhtml/session')->setFormData(false);

                // The following line decides if it is a "save" or "save and continue"
                if ($this->getRequest()->getParam('back')) {
                    $this->_redirect('*/*/edit', array('id' => $model->getId()));
                } else {
                    $this->_redirect('*/*/');
                }

            } catch (Exception $e) {
                Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
                if ($model && $model->getId()) {
                    $this->_redirect('*/*/edit', array('id' => $model->getId()));
                } else {
                    $this->_redirect('*/*/');
                }
            }

            return;
        }
        Mage::getSingleton('adminhtml/session')->addError(Mage::helper('awesome')->__('No data found to save'));
        $this->_redirect('*/*/');
    }

    public function deleteAction()
    {
        if ($id = $this->getRequest()->getParam('id')) {
            try {
                $model = Mage::getModel('awesome/example');
                $model->setId($id);
                $model->delete();
                Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('awesome')->__('The example has been deleted.'));
                $this->_redirect('*/*/');
                return;
            }
            catch (Exception $e) {
                Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
                $this->_redirect('*/*/edit', array('id' => $this->getRequest()->getParam('id')));
                return;
            }
        }
        Mage::getSingleton('adminhtml/session')->addError(Mage::helper('adminhtml')->__('Unable to find the example to delete.'));
        $this->_redirect('*/*/');
    }

}

The Grid Block
Here is where it starts getting a touch tricky. When you originally click on the menu item to see the grid of examples, you are going to the “indexAction” in the controller and simply loading and rendering the layout. This means that you will probably have something halfway useful to see in the awesome.xml layout file. We see that there is only one block defined for that handle, and that block is: ‘awesome/adminhtml_example’. This block extends Mage_Adminhtml_Block_Widget_Grid_Container which which tells us that our block (Super_Awesome_Block_Adminhtml_Example) will be be a container for a grid. What does that mean? This container will provide a few buttons at the top and automagically define the grid as a child block of itself. Below I will show you the entire contents of our container, and the piece that builds the name of the grid block (which is in the parent Mage_Adminhtml_Block_Widget_Grid_Container.

Super_Awesome_Block_Adminhtml_Example:

<?php

class Super_Awesome_Block_Adminhtml_Example extends Mage_Adminhtml_Block_Widget_Grid_Container
{
    protected $_addButtonLabel = 'Add New Example';

    public function __construct()
    {
        parent::__construct();
        $this->_controller = 'adminhtml_example';
        $this->_blockGroup = 'awesome';
        $this->_headerText = Mage::helper('awesome')->__('Examples');
    }

}
protected function _prepareLayout()
   {
       $this->setChild( 'grid',
           $this->getLayout()->createBlock( $this->_blockGroup.'/' . $this->_controller . '_grid',
           $this->_controller . '.grid')->setSaveParametersInSession(true) );
       return parent::_prepareLayout();

   }

Now that we have the container we need to build our grid (Super_Awesome_Block_Adminhtml_Example_Grid):

<?php

class Super_Awesome_Block_Adminhtml_Example_Grid extends Mage_Adminhtml_Block_Widget_Grid
{
    public function __construct()
    {
        parent::__construct();
        $this->setId('example_grid');
        $this->setDefaultSort('id');
        $this->setDefaultDir('desc');
        $this->setSaveParametersInSession(true);
    }

    protected function _prepareCollection()
    {
        $collection = Mage::getModel('awesome/example')->getCollection();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }

    protected function _prepareColumns()
    {
        $this->addColumn('id', array(
            'header'    => Mage::helper('awesome')->__('ID'),
            'align'     =>'right',
            'width'     => '50px',
            'index'     => 'id',
        ));

        $this->addColumn('name', array(
            'header'    => Mage::helper('awesome')->__('Name'),
            'align'     =>'left',
            'index'     => 'name',
        ));

        $this->addColumn('description', array(
            'header'    => Mage::helper('awesome')->__('Description'),
            'align'     =>'left',
            'index'     => 'description',
        ));

        $this->addColumn('other', array(
            'header'    => Mage::helper('awesome')->__('Other'),
            'align'     => 'left',
            'index'     => 'other',
        ));

        return parent::_prepareColumns();
    }

    public function getRowUrl($row)
    {
        return $this->getUrl('*/*/edit', array('id' => $row->getId()));
    }
}

The _prepareCollection() function gets the collection of data that will populate our grid, and the _prepareColumns() function maps that data into the specific columns. Keep in mind that the _prepareCollection() and the _prepareColumns() can be much more detailed/complicated than my example here, so don’t be afraid to try crazy things.

If you stop here, you should have a working grid.

The Forms
If you notice, the getRowUrl() on the grid returns back a url that maps to the editAction() in our controller. That is where were start the “form fun”. The editAction() handles both the “edit” scenario and the “new” scenario for the model. It makes no difference to us since it is the same form for both.

The edit action maps to a handle in the awesome.xml layout file which simply defines the block: awesome/adminhtml_example_edit. If we take a look at that block we will see the following code:

<?php

class Super_Awesome_Block_Adminhtml_Example_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
{
    public function __construct()
    {
        parent::__construct();

        $this->_objectId = 'id';
        $this->_blockGroup = 'awesome';
        $this->_controller = 'adminhtml_example';
        $this->_mode = 'edit';

        $this->_addButton('save_and_continue', array(
                  'label' => Mage::helper('adminhtml')->__('Save And Continue Edit'),
                  'onclick' => 'saveAndContinueEdit()',
                  'class' => 'save',
        ), -100);
        $this->_updateButton('save', 'label', Mage::helper('awesome')->__('Save Example'));

        $this->_formScripts[] = "
            function toggleEditor() {
                if (tinyMCE.getInstanceById('form_content') == null) {
                    tinyMCE.execCommand('mceAddControl', false, 'edit_form');
                } else {
                    tinyMCE.execCommand('mceRemoveControl', false, 'edit_form');
                }
            }

            function saveAndContinueEdit(){
                editForm.submit($('edit_form').action+'back/edit/');
            }
        ";
    }

    public function getHeaderText()
    {
        if (Mage::registry('example_data') && Mage::registry('example_data')->getId())
        {
            return Mage::helper('awesome')->__('Edit Example "%s"', $this->htmlEscape(Mage::registry('example_data')->getName()));
        } else {
            return Mage::helper('awesome')->__('New Example');
        }
    }


}

Just like the grid had a container, so does the form. We are just changing some labels on buttons and creating some javascript to handle the save scenarios. Below is the snippet of code in the parent container that builds the name of the block that will be rendered containing the actual form:

protected function _prepareLayout()
    {
        if ($this->_blockGroup && $this->_controller && $this->_mode) {
            $this->setChild('form', $this->getLayout()->createBlock($this->_blockGroup . '/' . $this->_controller . '_' . $this->_mode . '_form'));
        }
        return parent::_prepareLayout();

    }

Next/finally, we look at the actual form class; It’s so awesome, you might faint when you see it:

<?php

class Super_Awesome_Block_Adminhtml_Example_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        if (Mage::getSingleton('adminhtml/session')->getExampleData())
        {
            $data = Mage::getSingleton('adminhtml/session')->getExamplelData();
            Mage::getSingleton('adminhtml/session')->getExampleData(null);
        }
        elseif (Mage::registry('example_data'))
        {
            $data = Mage::registry('example_data')->getData();
        }
        else
        {
            $data = array();
        }

        $form = new Varien_Data_Form(array(
                'id' => 'edit_form',
                'action' => $this->getUrl('*/*/save', array('id' => $this->getRequest()->getParam('id'))),
                'method' => 'post',
                'enctype' => 'multipart/form-data',
        ));

        $form->setUseContainer(true);

        $this->setForm($form);

        $fieldset = $form->addFieldset('example_form', array(
             'legend' =>Mage::helper('awesome')->__('Example Information')
        ));

        $fieldset->addField('name', 'text', array(
             'label'     => Mage::helper('awesome')->__('Name'),
             'class'     => 'required-entry',
             'required'  => true,
             'name'      => 'name',
             'note'     => Mage::helper('awesome')->__('The name of the example.'),
        ));

        $fieldset->addField('description', 'text', array(
             'label'     => Mage::helper('awesome')->__('Description'),
             'class'     => 'required-entry',
             'required'  => true,
             'name'      => 'description',
        ));

        $fieldset->addField('other', 'text', array(
             'label'     => Mage::helper('awesome')->__('Other'),
             'class'     => 'required-entry',
             'required'  => true,
             'name'      => 'other',
        ));

        $form->setValues($data);

        return parent::_prepareForm();
    }

}

Did you faint?

The only thing not straight-forward here is the line: $form->setUseContainer(true);. This line is important because it is the line that actually causes the form renderer to output the surrounding <form> tags.

Conclusion
There ya have it. We now have a grid/form that allows us to manage the data in the super_awesome_example table. Hope this is helpful!

转载请注明:嗨酷哥,有你更酷! » Magento 开发后台管理Grids和Forms

与本文相关文章

发表我的评论

取消评论
表情 插代码

Hi,您需要填写昵称和邮箱!

  • 必填项
  • 必填项