Continuing on from adding a UI Grid Component in Magento 2, here’s how to add a Magento 2 admin form.
To start with, add a controller for the ‘add’ post action. This will simply forward to the ‘edit’ controller action, as we can use the same functionality for both actions.
// app/code/Siphor/News/Controller/Adminhtml/Post/Add.php
<?php
namespace Siphor\News\Controller\Adminhtml\Post;
use \Magento\Backend\App\Action\Context;
use \Magento\Backend\Model\View\Result\ForwardFactory;
use \Magento\Backend\App\Action;
class Add extends Action
{
protected $resultForwardFactory;
public function __construct(
Context $context,
ForwardFactory $resultForwardFactory
) {
$this->resultForwardFactory = $resultForwardFactory;
parent::__construct($context);
}
/**
* Forward to edit
*/
public function execute()
{
$resultForward = $this->resultForwardFactory->create();
return $resultForward->forward('edit');
}
}
The Edit.php controller will be responsible for loading the form.
// app/code/Siphor/News/Controller/Adminhtml/Post/Edit.php
<?php
namespace Siphor\News\Controller\Adminhtml\Post;
use \Magento\Backend\App\Action;
use \Magento\Backend\App\Action\Context;
use \Magento\Framework\View\Result\PageFactory;
use \Magento\Framework\Registry;
use \Magento\Framework\Message\Manager;
use \Siphor\News\Model\PostFactory;
class Edit extends Action
{
protected $_coreRegistry = null;
protected $resultPageFactory;
protected $_messageManager;
protected $_postFactory;
public function __construct(
Context $context,
PageFactory $resultPageFactory,
Registry $registry,
Manager $messageManager,
PostFactory $postFactory
) {
$this->resultPageFactory = $resultPageFactory;
$this->_coreRegistry = $registry;
$this->_messageManager = $messageManager;
$this->_postFactory = $postFactory;
parent::__construct($context);
}
protected function _initAction()
{
$resultPage = $this->resultPageFactory->create();
$resultPage->setActiveMenu('Siphor_News::news')
->addBreadcrumb(__('News'), __('News'))
->addBreadcrumb(__('Manage News Posts'), __('Manage News Posts'));
return $resultPage;
}
public function execute()
{
$id = $this->getRequest()->getParam('post_id');
$model = $this->_postFactory->create();
if ($id) {
$model->load($id);
if (!$model->getPostId()) {
$this->_messageManager->addErrorMessage(__('This post no longer exists.'));
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
}
}
$data = $this->_getSession()->getFormData(true);
if (!empty($data)) {
$model->setData($data);
}
$this->_coreRegistry->register('news_post', $model);
$resultPage = $this->_initAction();
$resultPage->addBreadcrumb(
$id ? __('Edit News Post') : __('New Post'),
$id ? __('Edit News Post') : __('New Post')
);
$resultPage->getConfig()->getTitle()->prepend(__('News Posts'));
$resultPage->getConfig()->getTitle()
->prepend($model->getPostId() ? $model->getPostTitle() : __('New Post'));
return $resultPage;
}
}
The Delete.php controller will delete a post when clicking on a ‘Delete’ button on the edit form page.
// app/code/Siphor/News/Controller/Adminhtml/Post/Delete.php
<?php
namespace Siphor\News\Controller\Adminhtml\Post;
use \Magento\Backend\App\Action;
use \Magento\Backend\App\Action\Context;
use \Magento\Framework\Message\Manager;
use \Siphor\News\Model\PostFactory;
class Delete extends Action
{
protected $_messageManager;
protected $_postFactory;
public function __construct(
Context $context,
Manager $messageManager,
PostFactory $postFactory
)
{
parent::__construct($context);
$this->_messageManager = $messageManager;
$this->_postFactory = $postFactory;
}
/**
* Delete action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$id = $this->getRequest()->getParam('post_id');
$resultRedirect = $this->resultRedirectFactory->create();
if ($id) {
try {
$model = $this->_postFactory->create();
$model->load($id);
$model->delete();
$this->_messageManager->addSuccessMessage(__('The post has been deleted.'));
return $resultRedirect->setPath('*/*/');
} catch (\Exception $e) {
$this->_messageManager->addErrorMessage($e->getMessage());
return $resultRedirect->setPath('*/*/edit', ['post_id' => $id]);
}
}
$this->_messageManager->addErrorMessage(__('We can\'t find the post to delete.'));
return $resultRedirect->setPath('*/*/');
}
}
And finally, the Save.php controller will save any posts we add or edit.
// app/code/Siphor/News/Controller/Adminhtml/Post/Save.php
<?php
namespace Siphor\News\Controller\Adminhtml\Post;
use \Magento\Backend\App\Action;
use \Magento\Backend\App\Action\Context;
use \Magento\Framework\Message\Manager;
use \Siphor\News\Model\PostFactory;
class Save extends Action
{
protected $_messageManager;
protected $_postFactory;
public function __construct(
Context $context,
Manager $messageManager,
PostFactory $postFactory
)
{
parent::__construct($context);
$this->_messageManager = $messageManager;
$this->_postFactory = $postFactory;
}
/**
* Save action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$data = $this->getRequest()->getPostValue();
$resultRedirect = $this->resultRedirectFactory->create();
if ($data) {
$model = $this->_postFactory->create();
$id = $this->getRequest()->getParam('post_id');
if ($id) {
$model->load($id);
}
$model->setData($data);
try {
$model->save();
$this->_messageManager->addSuccessMessage(__('You saved this Post.'));
$this->_getSession()->setFormData(false);
if ($this->getRequest()->getParam('back')) {
return $resultRedirect->setPath('*/*/edit', ['post_id' => $model->getId(), '_current' => true]);
}
return $resultRedirect->setPath('*/*/');
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->_messageManager->addErrorMessage($e->getMessage());
} catch (\RuntimeException $e) {
$this->_messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
$this->_messageManager->addException($e, __('Something went wrong while saving the post.'));
}
$this->_getSession()->setFormData($data);
return $resultRedirect->setPath('*/*/edit', ['post_id' => $this->getRequest()->getParam('post_id')]);
}
return $resultRedirect->setPath('*/*/');
}
}
Now that the controllers have been added, we need to focus on the block classes.
The edit form will have a layout handle of ‘news_post_edit’ (based on the action URL of ‘news/post/edit’), therefore create a news_post_edit.xml file that will instantiate the Edit.php block class.
// app/code/Siphor/News/view/adminhtml/layout/news_post_edit.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="editor"/>
<body>
<referenceContainer name="content">
<block class="Siphor\News\Block\Adminhtml\Post\Edit" name="news_post_edit" />
</referenceContainer>
</body>
</page>
The Edit.php class renders the form action buttons, header text and returns the ‘Save and Continue Edit’ URL.
// app/code/local/Siphor/News/Block/Adminhtml/Post/Edit.php
<?php
namespace Siphor\News\Block\Adminhtml\Post;
use \Magento\Backend\Block\Widget\Form\Container;
use \Magento\Backend\Block\Widget\Context;
use \Magento\Framework\Registry;
class Edit extends Container
{
protected $_coreRegistry = null;
public function __construct(
Context $context,
Registry $registry,
array $data = []
) {
$this->_coreRegistry = $registry;
parent::__construct($context, $data);
}
/**
* Initialize news post edit block
*
* @return void
*/
protected function _construct()
{
$this->_objectId = 'post_id';
$this->_blockGroup = 'Siphor_News';
$this->_controller = 'adminhtml_post';
parent::_construct();
$this->buttonList->update('delete', 'label', __('Delete Post'));
$this->buttonList->update('save', 'label', __('Save Post'));
$this->buttonList->add(
'saveandcontinue',
[
'label' => __('Save and Continue Edit'),
'class' => 'save',
'data_attribute' => [
'mage-init' => [
'button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form'],
],
]
],
-100
);
$this->buttonList->remove('reset');
}
public function getHeaderText()
{
if ($this->_coreRegistry->registry('news_post')->getId()) {
return __("Edit Post '%1'", $this->escapeHtml($this->_coreRegistry->registry('news_post')->getPostTitle()));
} else {
return __('New Post');
}
}
protected function _getSaveAndContinueUrl()
{
return $this->getUrl('news/*/save', ['_current' => true, 'back' => 'edit']);
}
}
The Form.php class renders the form elements.
// app/code/local/Siphor/News/Block/Adminhtml/Post/Edit/Form.php
<?php
namespace Siphor\News\Block\Adminhtml\Post\Edit;
use \Magento\Backend\Block\Widget\Form\Generic;
use \Magento\Backend\Block\Template\Context;
use \Magento\Framework\Registry;
use \Magento\Framework\Data\FormFactory;
use \Magento\Store\Model\System\Store;
use \Magento\Cms\Model\Wysiwyg\Config;
class Form extends Generic
{
protected $_systemStore;
protected $_wysiwygConfig;
/**
* Form constructor.
* @param Context $context
* @param Registry $registry
* @param FormFactory $formFactory
* @param Store $systemStore
* @param Config $wysiwygConfig
* @param array $data
*/
public function __construct(
Context $context,
Registry $registry,
FormFactory $formFactory,
Store $systemStore,
Config $wysiwygConfig,
array $data = []
) {
$this->_systemStore = $systemStore;
$this->_wysiwygConfig = $wysiwygConfig;
parent::__construct($context, $registry, $formFactory, $data);
}
/**
* Init form
*
* @return void
*/
protected function _construct()
{
parent::_construct();
$this->setId('post_form');
$this->setTitle(__('Post Information'));
}
protected function _prepareForm()
{
$model = $this->_coreRegistry->registry('news_post');
$form = $this->_formFactory->create(
['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
);
$form->setHtmlIdPrefix('post_');
$fieldset = $form->addFieldset(
'base_fieldset',
['legend' => __('General Information'), 'class' => 'fieldset-wide']
);
if ($model->getPostId()) {
$fieldset->addField('post_id', 'hidden', ['name' => 'post_id']);
}
$fieldset->addField(
'post_title',
'text',
['name' => 'post_title', 'label' => __('Post Title'), 'title' => __('Post Title'), 'required' => true]
);
$fieldset->addField(
'description',
'editor',
[
'name' => 'description',
'label' => __('Post Content'),
'title' => __('Post Content'),
'required' => true,
'style' => 'height:10em',
'config' => $this->_wysiwygConfig->getConfig()
]
);
$fieldset->addField(
'status',
'select',
[
'label' => __('Status'),
'title' => __('Status'),
'name' => 'status',
'required' => true,
'options' => ['1' => __('Enabled'), '0' => __('Disabled')]
]
);
if (!$model->getId()) {
$model->setData('status', '1');
}
$form->setValues($model->getData());
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
}
If you’ve followed all the steps correctly, you should have a fully functioning Magento 2 admin form.
Note: This article is based on Magento CE version 2.1.