Grid and Form in Magento 2 Admin Panel (Part 2)

Hi guys,
Today, I will continue introducing you how to create a Form via Magento 2 backend in custom module. We will continue using the Tutorial_SimpleNews module in the last post.
In order to understand this tutorial thoroughly, please review our last tutorials:
1. How to create a simple module in Magento 2
2. Create a module with custom database table in Magento 2
3. How to use Model and Collection in Magento 2
4. How to create the configuration via backend for a custom module
5. Adding new menu item via backend in custom module
6. Grid and Form in Magento 2 Admin Panel (Part 1)
Ok, now let’s get started!
Step 1: Create layout files.
- Create file: app/code/Tutorial/SimpleNews/view/adminhtml/layout/simplenews_news_create.xml and insert this following code into it:
<?xml version="1.0"?> <layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../Magento/Core/etc/layout_single.xsd"> <update handle="simplenews_news_edit"/> </layout>
- Create file: app/code/Tutorial/SimpleNews/view/adminhtml/layout/simplenews_news_edit.xml (Purpose: This file is used to declare blocks which used on editing page) and insert this following code into it:
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="../../../../../../ ../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="left"> <block class="Tutorial\SimpleNews\Block\Adminhtml\News\Edit\Tabs" name="tutorial_simplenews_news.edit.tabs"/> </referenceContainer> <referenceContainer name="content"> <block class="Tutorial\SimpleNews\Block\Adminhtml\News\Edit" name="tutorial_simplenews_news.edit"/> </referenceContainer> </body> </page>
Step 2: Create block files.
- Create file: app/code/Tutorial/SimpleNews/Block/Adminhtml/News/Edit.php (Purpose: This is the block file of form container) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Block\Adminhtml\News; use Magento\Backend\Block\Widget\Form\Container; use Magento\Backend\Block\Widget\Context; use Magento\Framework\Registry; class Edit extends Container { /** * Core registry * * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; /** * @param Context $context * @param Registry $registry * @param array $data */ public function __construct( Context $context, Registry $registry, array $data = [] ) { $this->_coreRegistry = $registry; parent::__construct($context, $data); } /** * Class constructor * * @return void */ protected function _construct() { $this->_objectId = 'id'; $this->_controller = 'adminhtml_news'; $this->_blockGroup = 'Tutorial_SimpleNews'; parent::_construct(); $this->buttonList->update('save', 'label', __('Save')); $this->buttonList->add( 'saveandcontinue', [ 'label' => __('Save and Continue Edit'), 'class' => 'save', 'data_attribute' => [ 'mage-init' => [ 'button' => [ 'event' => 'saveAndContinueEdit', 'target' => '#edit_form' ] ] ] ], -100 ); $this->buttonList->update('delete', 'label', __('Delete')); } /** * Retrieve text for header element depending on loaded news * * @return string */ public function getHeaderText() { $newsRegistry = $this->_coreRegistry->registry('simplenews_news'); if ($newsRegistry->getId()) { $newsTitle = $this->escapeHtml($newsRegistry->getTitle()); return __("Edit News '%1'", $newsTitle); } else { return __('Add News'); } } /** * Prepare layout * * @return \Magento\Framework\View\Element\AbstractBlock */ protected function _prepareLayout() { $this->_formScripts[] = " function toggleEditor() { if (tinyMCE.getInstanceById('post_content') == null) { tinyMCE.execCommand('mceAddControl', false, 'post_content'); } else { tinyMCE.execCommand('mceRemoveControl', false, 'post_content'); } }; "; return parent::_prepareLayout(); } }
- Create file: app/code/Tutorial/SimpleNews/Block/Adminhtml/News/Edit/Tabs.php (Purpose: This file will declare tabs at left column of the editing page) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Block\Adminhtml\News\Edit; use Magento\Backend\Block\Widget\Tabs as WidgetTabs; class Tabs extends WidgetTabs { /** * Class constructor * * @return void */ protected function _construct() { parent::_construct(); $this->setId('news_edit_tabs'); $this->setDestElementId('edit_form'); $this->setTitle(__('News Information')); } /** * @return $this */ protected function _beforeToHtml() { $this->addTab( 'news_info', [ 'label' => __('General'), 'title' => __('General'), 'content' => $this->getLayout()->createBlock( 'Tutorial\SimpleNews\Block\Adminhtml\News\Edit\Tab\Info' )->toHtml(), 'active' => true ] ); return parent::_beforeToHtml(); } }
- Create file: app/code/Tutorial/SimpleNews/Block/Adminhtml/News/Edit/Form.php (Purpose: This file will declare form information) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Block\Adminhtml\News\Edit; use Magento\Backend\Block\Widget\Form\Generic; class Form extends Generic { /** * @return $this */ protected function _prepareForm() { /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create( [ 'data' => [ 'id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post' ] ] ); $form->setUseContainer(true); $this->setForm($form); return parent::_prepareForm(); } }
- Create file: app/code/Tutorial/SimpleNews/Block/Adminhtml/News/Edit/Tab/Info.php (Purpose: This file will declare fields in form) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Block\Adminhtml\News\Edit\Tab; use Magento\Backend\Block\Widget\Form\Generic; use Magento\Backend\Block\Widget\Tab\TabInterface; use Magento\Backend\Block\Template\Context; use Magento\Framework\Registry; use Magento\Framework\Data\FormFactory; use Magento\Cms\Model\Wysiwyg\Config; use Tutorial\SimpleNews\Model\System\Config\Status; class Info extends Generic implements TabInterface { /** * @var \Magento\Cms\Model\Wysiwyg\Config */ protected $_wysiwygConfig; /** * @var \Tutorial\SimpleNews\Model\Config\Status */ protected $_newsStatus; /** * @param Context $context * @param Registry $registry * @param FormFactory $formFactory * @param Config $wysiwygConfig * @param Status $newsStatus * @param array $data */ public function __construct( Context $context, Registry $registry, FormFactory $formFactory, Config $wysiwygConfig, Status $newsStatus, array $data = [] ) { $this->_wysiwygConfig = $wysiwygConfig; $this->_newsStatus = $newsStatus; parent::__construct($context, $registry, $formFactory, $data); } /** * Prepare form fields * * @return \Magento\Backend\Block\Widget\Form */ protected function _prepareForm() { /** @var $model \Tutorial\SimpleNews\Model\News */ $model = $this->_coreRegistry->registry('simplenews_news'); /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create(); $form->setHtmlIdPrefix('news_'); $form->setFieldNameSuffix('news'); $fieldset = $form->addFieldset( 'base_fieldset', ['legend' => __('General')] ); if ($model->getId()) { $fieldset->addField( 'id', 'hidden', ['name' => 'id'] ); } $fieldset->addField( 'title', 'text', [ 'name' => 'title', 'label' => __('Title'), 'required' => true ] ); $fieldset->addField( 'status', 'select', [ 'name' => 'status', 'label' => __('Status'), 'options' => $this->_newsStatus->toOptionArray() ] ); $fieldset->addField( 'summary', 'textarea', [ 'name' => 'summary', 'label' => __('Summary'), 'required' => true, 'style' => 'height: 15em; width: 30em;' ] ); $wysiwygConfig = $this->_wysiwygConfig->getConfig(); $fieldset->addField( 'description', 'editor', [ 'name' => 'description', 'label' => __('Description'), 'required' => true, 'config' => $wysiwygConfig ] ); $data = $model->getData(); $form->setValues($data); $this->setForm($form); return parent::_prepareForm(); } /** * Prepare label for tab * * @return string */ public function getTabLabel() { return __('News Info'); } /** * Prepare title for tab * * @return string */ public function getTabTitle() { return __('News Info'); } /** * {@inheritdoc} */ public function canShowTab() { return true; } /** * {@inheritdoc} */ public function isHidden() { return false; } }
Step 3: Create controller files.
- Create file: app/code/Tutorial/SimpleNews/Controller/Adminhtml/News/NewAction.php (Purpose: This is the new action) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Controller\Adminhtml\News; use Tutorial\SimpleNews\Controller\Adminhtml\News; class NewAction extends News { /** * Create new news action * * @return void */ public function execute() { $this->_forward('edit'); } }
- Create file: app/code/Tutorial/SimpleNews/Controller/Adminhtml/News/Edit.php (Purpose: This is the edit action for editing news page) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Controller\Adminhtml\News; use Tutorial\SimpleNews\Controller\Adminhtml\News; class Edit extends News { /** * @return void */ public function execute() { $newsId = $this->getRequest()->getParam('id'); /** @var \Tutorial\SimpleNews\Model\News $model */ $model = $this->_newsFactory->create(); if ($newsId) { $model->load($newsId); if (!$model->getId()) { $this->messageManager->addError(__('This news no longer exists.')); $this->_redirect('*/*/'); return; } } // Restore previously entered form data from session $data = $this->_session->getNewsData(true); if (!empty($data)) { $model->setData($data); } $this->_coreRegistry->register('simplenews_news', $model); /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->_resultPageFactory->create(); $resultPage->setActiveMenu('Tutorial_SimpleNews::main_menu'); $resultPage->getConfig()->getTitle()->prepend(__('Simple News')); return $resultPage; } }
- Create file: app/code/Tutorial/SimpleNews/Controller/Adminhtml/News/Save.php (Purpose: This is the save action) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Controller\Adminhtml\News; use Tutorial\SimpleNews\Controller\Adminhtml\News; class Save extends News { /** * @return void */ public function execute() { $isPost = $this->getRequest()->getPost(); if ($isPost) { $newsModel = $this->_newsFactory->create(); $newsId = $this->getRequest()->getParam('id'); if ($newsId) { $newsModel->load($newsId); } $formData = $this->getRequest()->getParam('news'); $newsModel->setData($formData); try { // Save news $newsModel->save(); // Display success message $this->messageManager->addSuccess(__('The news has been saved.')); // Check if 'Save and Continue' if ($this->getRequest()->getParam('back')) { $this->_redirect('*/*/edit', ['id' => $newsModel->getId(), '_current' => true]); return; } // Go to grid page $this->_redirect('*/*/'); return; } catch (\Exception $e) { $this->messageManager->addError($e->getMessage()); } $this->_getSession()->setFormData($formData); $this->_redirect('*/*/edit', ['id' => $newsId]); } } }
- Create file: app/code/Tutorial/SimpleNews/Controller/Adminhtml/News/Delete.php (Purpose: This is the delete action) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Controller\Adminhtml\News; use Tutorial\SimpleNews\Controller\Adminhtml\News; class Delete extends News { /** * @return void */ public function execute() { $newsId = (int) $this->getRequest()->getParam('id'); if ($newsId) { /** @var $newsModel \Mageworld\SimpleNews\Model\News */ $newsModel = $this->_newsFactory->create(); $newsModel->load($newsId); // Check this news exists or not if (!$newsModel->getId()) { $this->messageManager->addError(__('This news no longer exists.')); } else { try { // Delete news $newsModel->delete(); $this->messageManager->addSuccess(__('The news has been deleted.')); // Redirect to grid page $this->_redirect('*/*/'); return; } catch (\Exception $e) { $this->messageManager->addError($e->getMessage()); $this->_redirect('*/*/edit', ['id' => $newsModel->getId()]); } } } } }
- Create file: app/code/Tutorial/SimpleNews/Controller/Adminhtml/News/MassDelete.php (Purpose: This file is used for deleting multi items on grid) and insert this following code into it:
<?php namespace Tutorial\SimpleNews\Controller\Adminhtml\News; use Tutorial\SimpleNews\Controller\Adminhtml\News; class MassDelete extends News { /** * @return void */ public function execute() { // Get IDs of the selected news $newsIds = $this->getRequest()->getParam('news'); foreach ($newsIds as $newsId) { try { /** @var $newsModel \Mageworld\SimpleNews\Model\News */ $newsModel = $this->_newsFactory->create(); $newsModel->load($newsId)->delete(); } catch (\Exception $e) { $this->messageManager->addError($e->getMessage()); } } if (count($newsIds)) { $this->messageManager->addSuccess( __('A total of %1 record(s) were deleted.', count($newsIds)) ); } $this->_redirect('*/*/index'); } }
Ok, it’s done. You can go to backend site then access Simple News > Add News menu to see your result:
Finally, we have finished this tutorial, and I hope it’s useful for you. In next tutorials, I will introduce you the remaining of this tutorial “Simple News module: Working with Magento 2 Frontend”. See you again in our next Magento 2 tutorial.
Enjoy Magento 2 challenge with MageWorld ‘s tutorials! Follow our facebook fanpage for further discussion.
Related Posts

Grid and Form in Magento 2 Admin Panel (Part 1)

Adding new menu item in Magento 2 custom module

How to create the configuration via backend for a custom module
