如何在Magento 2中不使用Ui Component创建后台表单、CRUD及Grid按钮?
Hey there! I’ve worked through these exact requirements before, so let’s break this down into actionable steps that skip the UI Component overhead. We’ll start with your existing Grid, then move into building the full form CRUD flow.
Part 1: Add "New" Button & Row Action Buttons to Your Existing Grid
You already have a Grid showing your data, so let’s add the missing buttons first.
1. Add the "Add New Item" Button to the Grid Page
Open your Grid block class (usually Vendor/Module/Block/Adminhtml/YourEntity/Grid.php) and override the _prepareLayout() method to inject the button:
protected function _prepareLayout() { $addButtonProps = [ 'label' => __('Add New Item'), 'onclick' => "setLocation('" . $this->getUrl('*/*/new') . "')", 'class' => 'primary add' ]; $this->setChild('add_button', $this->getLayout()->createBlock(\Magento\Backend\Block\Widget\Button::class)->setData($addButtonProps)); return parent::_prepareLayout(); }
Then, in your Grid’s template file (Vendor/Module/view/adminhtml/templates/grid.phtml), add this line where you want the button to appear (usually above the Grid):
<?= $this->getChildHtml('add_button') ?>
2. Add Edit/Delete Action Buttons to Each Grid Row
In the same Grid block’s _prepareColumns() method, add an "Actions" column at the end:
$this->addColumn( 'action', [ 'header' => __('Actions'), 'type' => 'action', 'getter' => 'getId', 'actions' => [ [ 'caption' => __('Edit'), 'url' => ['base' => '*/*/edit'], 'field' => 'id' ], [ 'caption' => __('Delete'), 'url' => ['base' => '*/*/delete'], 'field' => 'id', 'confirm' => [ 'title' => __('Delete Item'), 'message' => __('Are you sure you want to delete this item?') ] ] ], 'filter' => false, 'sortable' => false, 'index' => 'id', 'header_css_class' => 'col-action', 'column_css_class' => 'col-action' ] );
This will render edit and delete buttons for every row, with a confirmation prompt for deletes.
Part 2: Full CRUD Flow with Admin Form (No UI Components)
Now let’s build the form and the backend logic to handle create, read, update, and delete operations using traditional Magento 2 block/model/controller patterns.
1. Create Your Entity’s Model, ResourceModel, and Collection
First, we need the data layer to interact with your database table:
- Model Class (
Vendor/Module/Model/YourEntity.php):
namespace Vendor\Module\Model; use Magento\Framework\Model\AbstractModel; class YourEntity extends AbstractModel { protected function _construct() { $this->_init(\Vendor\Module\Model\ResourceModel\YourEntity::class); } }
- ResourceModel Class (
Vendor/Module/Model/ResourceModel/YourEntity.php):
namespace Vendor\Module\Model\ResourceModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; class YourEntity extends AbstractDb { protected function _construct() { $this->_init('your_entity_table', 'id'); // Replace with your table name and primary key } }
- Collection Class (
Vendor/Module/Model/ResourceModel/YourEntity/Collection.php):
namespace Vendor\Module\Model\ResourceModel\YourEntity; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; class Collection extends AbstractCollection { protected $_idFieldName = 'id'; protected function _construct() { $this->_init( \Vendor\Module\Model\YourEntity::class, \Vendor\Module\Model\ResourceModel\YourEntity::class ); } }
2. Build the Admin Form Blocks
We need two blocks: one for the form container (with save buttons) and one for the form fields themselves.
- Form Container Block (
Vendor/Module/Block/Adminhtml/YourEntity/Edit.php):
namespace Vendor\Module\Block\Adminhtml\YourEntity; use Magento\Backend\Block\Widget\Form\Container; class Edit extends Container { protected function _construct() { $this->_objectId = 'id'; $this->_controller = 'adminhtml_yourEntity'; $this->_blockGroup = 'Vendor_Module'; parent::_construct(); $this->buttonList->update('save', 'label', __('Save Item')); $this->buttonList->add( 'saveandcontinue', [ 'label' => __('Save and Continue Edit'), 'class' => 'save', 'data_attribute' => [ 'mage-init' => [ 'button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form'], ], ] ], -100 ); } public function getHeaderText() { if ($this->_coreRegistry->registry('your_entity')->getId()) { return __("Edit Item '%1'", $this->escapeHtml($this->_coreRegistry->registry('your_entity')->getName())); } else { return __('New Item'); } } }
- Form Content Block (
Vendor/Module/Block/Adminhtml/YourEntity/Edit/Form.php):
namespace Vendor\Module\Block\Adminhtml\YourEntity\Edit; use Magento\Backend\Block\Widget\Form\Generic; class Form extends Generic { protected function _prepareForm() { $model = $this->_coreRegistry->registry('your_entity'); $form = $this->_formFactory->create( [ 'data' => [ 'id' => 'edit_form', 'action' => $this->getUrl('*/*/save'), 'method' => 'post', 'enctype' => 'multipart/form-data' // Add this if you need file uploads ] ] ); $form->setHtmlIdPrefix('your_entity_'); $fieldset = $form->addFieldset( 'base_fieldset', ['legend' => __('Item Information'), 'class' => 'fieldset-wide'] ); if ($model->getId()) { $fieldset->addField('id', 'hidden', ['name' => 'id']); } // Add your custom fields here—example below $fieldset->addField( 'name', 'text', [ 'name' => 'name', 'label' => __('Name'), 'title' => __('Name'), 'required' => true ] ); $fieldset->addField( 'description', 'textarea', [ 'name' => 'description', 'label' => __('Description'), 'title' => __('Description'), 'required' => false ] ); $form->setValues($model->getData()); $form->setUseContainer(true); $this->setForm($form); return parent::_prepareForm(); } }
3. Create Admin Controllers for CRUD Actions
These controllers handle the actual requests for creating, editing, saving, and deleting items.
- New Item Controller (
Vendor/Module/Controller/Adminhtml/YourEntity/NewAction.php):
namespace Vendor\Module\Controller\Adminhtml\YourEntity; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Magento\Framework\View\Result\PageFactory; class NewAction extends Action { protected $resultPageFactory; public function __construct(Context $context, PageFactory $resultPageFactory) { $this->resultPageFactory = $resultPageFactory; parent::__construct($context); } public function execute() { $resultPage = $this->resultPageFactory->create(); $resultPage->setActiveMenu('Vendor_Module::your_entity'); $resultPage->getConfig()->getTitle()->prepend(__('New Item')); return $resultPage; } protected function _isAllowed() { return $this->_authorization->isAllowed('Vendor_Module::your_entity_save'); } }
- Edit Item Controller (
Vendor/Module/Controller/Adminhtml/YourEntity/Edit.php):
namespace Vendor\Module\Controller\Adminhtml\YourEntity; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Magento\Framework\View\Result\PageFactory; use Vendor\Module\Model\YourEntityFactory; use Magento\Framework\Registry; class Edit extends Action { protected $resultPageFactory; protected $yourEntityFactory; protected $coreRegistry; public function __construct( Context $context, PageFactory $resultPageFactory, YourEntityFactory $yourEntityFactory, Registry $coreRegistry ) { $this->resultPageFactory = $resultPageFactory; $this->yourEntityFactory = $yourEntityFactory; $this->coreRegistry = $coreRegistry; parent::__construct($context); } public function execute() { $id = $this->getRequest()->getParam('id'); $model = $this->yourEntityFactory->create(); if ($id) { $model->load($id); if (!$model->getId()) { $this->messageManager->addErrorMessage(__('This item no longer exists.')); $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('*/*/'); } } $this->coreRegistry->register('your_entity', $model); $resultPage = $this->resultPageFactory->create(); $resultPage->setActiveMenu('Vendor_Module::your_entity'); $resultPage->getConfig()->getTitle()->prepend($model->getId() ? __('Edit Item') : __('New Item')); return $resultPage; } protected function _isAllowed() { return $this->_authorization->isAllowed('Vendor_Module::your_entity_save'); } }
- Save Item Controller (
Vendor/Module/Controller/Adminhtml/YourEntity/Save.php):
namespace Vendor\Module\Controller\Adminhtml\YourEntity; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Vendor\Module\Model\YourEntityFactory; use Magento\Framework\Exception\LocalizedException; class Save extends Action { protected $yourEntityFactory; public function __construct(Context $context, YourEntityFactory $yourEntityFactory) { $this->yourEntityFactory = $yourEntityFactory; parent::__construct($context); } public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); $data = $this->getRequest()->getPostValue(); if ($data) { $id = $this->getRequest()->getParam('id'); $model = $this->yourEntityFactory->create()->load($id); if (!$model->getId() && $id) { $this->messageManager->addErrorMessage(__('This item no longer exists.')); return $resultRedirect->setPath('*/*/'); } $model->setData($data); try { $model->save(); $this->messageManager->addSuccessMessage(__('You saved the item.')); if ($this->getRequest()->getParam('back')) { return $resultRedirect->setPath('*/*/edit', ['id' => $model->getId()]); } return $resultRedirect->setPath('*/*/'); } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the item.')); } $this->_getSession()->setFormData($data); return $resultRedirect->setPath('*/*/edit', ['id' => $id]); } return $resultRedirect->setPath('*/*/'); } protected function _isAllowed() { return $this->_authorization->isAllowed('Vendor_Module::your_entity_save'); } }
- Delete Item Controller (
Vendor/Module/Controller/Adminhtml/YourEntity/Delete.php):
namespace Vendor\Module\Controller\Adminhtml\YourEntity; use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Vendor\Module\Model\YourEntityFactory; use Magento\Framework\Exception\LocalizedException; class Delete extends Action { protected $yourEntityFactory; public function __construct(Context $context, YourEntityFactory $yourEntityFactory) { $this->yourEntityFactory = $yourEntityFactory; parent::__construct($context); } public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); $id = $this->getRequest()->getParam('id'); if ($id) { try { $model = $this->yourEntityFactory->create(); $model->load($id); $model->delete(); $this->messageManager->addSuccessMessage(__('You deleted the item.')); return $resultRedirect->setPath('*/*/'); } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong while deleting the item.')); return $resultRedirect->setPath('*/*/edit', ['id' => $id]); } } $this->messageManager->addErrorMessage(__('We can\'t find the item to delete.')); return $resultRedirect->setPath('*/*/'); } protected function _isAllowed() { return $this->_authorization->isAllowed('Vendor_Module::your_entity_delete'); } }
4. Configure Admin Routes & Permissions
- Admin Route (
Vendor/Module/etc/adminhtml/routes.xml):
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="admin"> <route id="yourmodule" frontName="yourmodule"> <module name="Vendor_Module" before="Magento_Backend"/> </route> </router> </config>
- ACL Permissions (
Vendor/Module/etc/acl.xml):
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> <acl> <resources> <resource id="Magento_Backend::admin"> <resource id="Vendor_Module::your_entity" title="Your Entity" sortOrder="10"> <resource id="Vendor_Module::your_entity_save" title="Save" sortOrder="10"/> <resource id="Vendor_Module::your_entity_delete" title="Delete" sortOrder="20"/> </resource> </resource> </resources> </acl> </config>
5. Add Admin Menu (Optional)
If you want your module to show up in the admin sidebar, add this menu config (Vendor/Module/etc/adminhtml/menu.xml):
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> <add id="Vendor_Module::your_entity" title="Your Entity" module="Vendor_Module" sortOrder="50" parent="Magento_Backend::content" action="yourmodule/yourentity"/> </menu> </config>
Part 3: Test Everything Out
- Run these commands to apply your changes:
bin/magento setup:upgrade bin/magento cache:flush
- Log into your Magento admin,

