I'm trying to create a Magento extension but I'm getting an issue when accessing a custom form for submitting a new entry. The grid for showing results works fine but clicking the add new button from the grid results in an error where it's looking for the form in the wrong place (not the extension directory). I'm obviously overlooking something but I have spend hours checking everthing. I really hope someone can help pinpoint this.
The error:
Exception #0 (Magento\Framework\Exception\LocalizedException): Invalid block type: Magento\Backend\Block\Adminhtml\Menu\Edit\Form
Exception #1 (ReflectionException): Class Magento\Backend\Block\Adminhtml\Menu\Edit\Form does not exist
Layout: \app\code\Vendor\Customizablemenus\view\adminhtml\layout\customizablemenus_menu_edit.xml .
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="editor"/>
<head>
<script src="Vendor_Customizablemenus::js/customizablemenus-admin.js" />
<css src="Vendor_Customizablemenus::css/category-selector.css" />
<css src="Vendor_Customizablemenus::css/spectrum.css" />
<css src="Vendor_Customizablemenus::css/customizablemenus.css" />
</head>
<body>
<referenceBlock name="menu">
<action method="setActive">
<argument name="itemId" xsi:type="string">Vendor_Customizablemenus::manager</argument>
</action>
</referenceBlock>
<referenceContainer name="content">
<block class="Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit" name="customizablemenus_menu_edit" />
</referenceContainer>
<referenceContainer name="left">
<block class="Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit\Tabs" name="customizablemenus_menu_edit_tabs">
<block class="Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit\Tab\Menu" name="customizablemenus_menu_edit_tab_menu" />
<action method="addTab">
<argument name="name" xsi:type="string">main_section</argument>
<argument name="block" xsi:type="string">customizablemenus_menu_edit_tab_menu</argument>
</action>
</block>
</referenceContainer>
</body>
</page>
Controller: \app\code\Vendor\Customizablemenus\Controller\Adminhtml\Menu\Edit.php .
namespace Vendor\Customizablemenus\Controller\Adminhtml\Menu;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\Registry;
class Edit extends \Magento\Backend\App\Action
{
/**
* Core registry.
*
* @var Registry
*/
protected $_coreRegistry = null;
/**
* Variable.
*
* @var PageFactory
*/
protected $resultPageFactory;
/**
* Construct.
*
* @param Context $context Context.
* @param PageFactory $resultPageFactory ResultPageFactory.
* @param Registry $registry Registry.
*/
public function __construct(
Context $context,
PageFactory $resultPageFactory,
Registry $registry
) {
$this->resultPageFactory = $resultPageFactory;
$this->_coreRegistry = $registry;
parent::__construct($context);
}
/**
* {@inheritdoc}
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('Vendor_Customizablemenus::manager');
}
/**
* Init actions.
*
* @return \Magento\Backend\Model\View\Result\Page
*/
protected function _initAction()
{
$resultPage = $this->resultPageFactory->create();
return $resultPage;
}
/**
* Edit Customizablemenus menu.
*
* @return \Magento\Backend\Model\View\Result\Page
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function execute()
{
$id = $this->getRequest()->getParam('menu_id');
$model = $this->_objectManager->create(\Vendor\Customizablemenus\Model\Menu::class);
if ($id) {
$model->load($id);
if (!$model->getId()) {
$this->messageManager->addError(__('This menu no longer exists.'));
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
}
}
$data = $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getFormData(true);
if (!empty($data)) {
$model->setData($data);
}
$this->_coreRegistry->register('customizablemenus_menu', $model);
$resultPage = $this->_initAction();
$resultPage->getConfig()->getTitle()->prepend(__('Menu Management'));
$resultPage->getConfig()->getTitle()
->prepend($model->getId() ? sprintf(__('Edit Menu "%s"'), $model->getTitle()) : __('New Menu'));
return $resultPage;
}
}
Block: \app\code\Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit.php
namespace Vendor\Customizablemenus\Block\Adminhtml\Menu;
/**
* Customizablemenus Admin Menu Page
*/
class Edit extends \Magento\Backend\Block\Widget\Form\Container
{
/**
* Variable.
*
* @var \Magento\Framework\Registry
*/
protected $_coreRegistry = null;
/**
* Construct.
*
* @param \Magento\Backend\Block\Widget\Context $context Context.
* @param \Magento\Framework\Registry $registry CoreRegistry.
* @param array $data Data.
*/
public function __construct(
\Magento\Backend\Block\Widget\Context $context,
\Magento\Framework\Registry $registry,
array $data = []
) {
$this->_coreRegistry = $registry;
parent::__construct($context, $data);
}
/**
* Initialize customizablemenus menu edit block.
*
* @return void
*/
protected function _construct()
{
$this->_objectId = 'menu_id';
$this->_blockMenu = 'Vendor_Customizablemenus';
$this->_controller = 'adminhtml_menu';
parent::_construct();
if ($this->_isAllowedAction('Vendor_Customizablemenus::manager')) {
$this->buttonList->update('save', 'label', __('Save Menu'));
$this->buttonList->add(
'saveandcontinue',
[
'label' => __('Save and Continue Edit'),
'class' => 'save',
'data_attribute' => [
'mage-init' => [
'button' => [
'event' => 'saveAndContinueEdit',
'target' => '#edit_form'
],
],
]
],
-100
);
} else {
$this->buttonList->remove('save');
}
if ($this->_isAllowedAction('Vendor_Customizablemenus::manager')) {
$this->buttonList->update('delete', 'label', __('Delete Menu'));
} else {
$this->buttonList->remove('delete');
}
}
/**
* Prepare collection.
*
* @param string $resourceId Resource ID.
*
* @return bool
*/
protected function _isAllowedAction($resourceId)
{
return $this->_authorization->isAllowed($resourceId);
}
/**
* Getter of url for "Save and Continue" button.
*
* @return string
*/
protected function _getSaveAndContinueUrl()
{
return $this->getUrl(
'*/*/save',
['_current' => true, 'back' => 'edit', 'tab' => '{{tab_id}}']
);
}
}
Form:\app\code\Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit\Form.php .
namespace Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit;
/**
* Adminhtml customizablemenus menu edit form block
*/
class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
/**
* Prepare form.
*
* @return $this
*/
protected function _prepareForm()
{
$form = $this->_formFactory->create(
[
'data' => [
'id' => 'edit_form',
'action' => $this->getUrl('*/*/save'),
'method' => 'post',
'enctype' => 'multipart/form-data',
],
]
);
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
}
Menu: app\code\Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit\Tab\Menu.php .
namespace Vendor\Customizablemenus\Block\Adminhtml\Menu\Edit\Tab;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Registry;
use Magento\Framework\Data\FormFactory;
use Magento\Store\Model\System\Store;
use Magento\Config\Model\Config\Structure\Element\Dependency\FieldFactory;
/**
* Customizablemenus Menu form block
*/
class Menu extends \Magento\Backend\Block\Widget\Form\Generic implements \Magento\Backend\Block\Widget\Tab\TabInterface
{
/**
* Variable.
*
* @var FieldFactory
*/
protected $fieldFactory;
/**
* Variable.
*
* @var Store
*/
protected $systemStore;
/**
* Variable.
*
* @var Options
*/
protected $customizablemenusHelper;
/**
* Construct.
*
* @param Context $context Context.
* @param Registry $registry Registry.
* @param FormFactory $formFactory Context.
* @param Store $systemStore Systemstore.
* @param FieldFactory $fieldFactory FormFactory.
* @param array $data Data.
*/
public function __construct(
Context $context,
Registry $registry,
FormFactory $formFactory,
Store $systemStore,
FieldFactory $fieldFactory,
array $data = []
) {
$this->_localeDate = $context->getLocaleDate();
$this->systemStore = $systemStore;
$this->fieldFactory = $fieldFactory;
parent::__construct($context, $registry, $formFactory, $data);
}
/**
* Prepare form.
*
* @return $this
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function _prepareForm()
{
$model = $this->_coreRegistry->registry('customizablemenus_menu');
$form = $this->_formFactory->create();
$form->setHtmlIdPrefix('customizablemenus_');
$dependenceBlock = $this->getLayout()
->createBlock(\Magento\Backend\Block\Widget\Form\Element\Dependence::class);
$fieldMaps = [];
$fieldset = $form->addFieldset(
'general_fieldset',
['legend' => __('General')]
);
if ($model->getId()) {
$fieldset->addField('menu_id', 'hidden', ['name' => 'menu_id']);
}
$fieldMaps['title'] = $fieldset->addField(
'title',
'text',
[
'name' => 'title',
'label' => __('Title'),
'title' => __('Title'),
'required' => true,
'class' => 'required-entry',
]
);
$fieldMaps['identifier'] = $fieldset->addField(
'identifier',
'text',
[
'name' => 'identifier',
'label' => __('Identifier'),
'title' => __('Identifier'),
'note' => __('a unique identifier that is used to
inject the slide menu via XML'),
'required' => true,
'class' => 'required-entry validate-code',
]
);
$fieldMaps['is_active'] = $fieldset->addField(
'is_active',
'select',
[
'name' => 'is_active',
'label' => __('Status'),
'title' => __('Status'),
'required' => true,
'options' => $model->getAvailableStatuses()
]
);
if (!$this->_storeManager->isSingleStoreMode()) {
$field = $fieldset->addField(
'store_id',
'multiselect',
[
'name' => 'stores[]',
'label' => __('Store View'),
'title' => __('Store View'),
'required' => true,
'values' => $this->systemStore
->getStoreValuesForForm(false, true)
]
);
$renderer = $this
->getLayout()
->createBlock(\Magento\Backend\Block\Store\Switcher\Form\Renderer\Fieldset\Element::class);
$field->setRenderer($renderer);
} else {
$fieldset->addField(
'store_id',
'hidden',
[
'name' => 'stores[]',
'value' => $this->_storeManager->getStore(true)->getId()
]
);
$model->setStoreId($this->_storeManager->getStore(true)->getId());
}
foreach ($fieldMaps as $fieldMap) {
$dependenceBlock->addFieldMap(
$fieldMap->getHtmlId(),
$fieldMap->getName()
);
}
$mappingFieldDependence = $this->getMappingFieldDependence();
foreach ($mappingFieldDependence as $dep) {
$negative = isset($dep['negative']) && $dep['negative'];
if (is_array($dep['fieldName'])) {
foreach ($dep['fieldName'] as $fieldName) {
$dependenceBlock->addFieldDependence(
$fieldMaps[$fieldName]->getName(),
$fieldMaps[$dep['fieldNameFrom']]->getName(),
$this->getDependencyField($dep['refField'], $negative)
);
}
} else {
$dependenceBlock->addFieldDependence(
$fieldMaps[$dep['fieldName']]->getName(),
$fieldMaps[$dep['fieldNameFrom']]->getName(),
$this->getDependencyField($dep['refField'], $negative)
);
}
}
$this->setChild('form_after', $dependenceBlock);
$defaultData = [
'is_active' => 1,
];
if (!$model->getId()) {
$model->addData($defaultData);
}
$form->setValues($model->getData());
$this->setForm($form);
return parent::_prepareForm();
}
/**
* Get field dependencies.
*
* @param string $refField Reference field for field dependency.
* @param bool $negative Invert selection of reference.
* @param string $separator Field seperator.
* @param string $fieldPrefix Field prefix.
*
* @return $this->fieldFactory
*/
public function getDependencyField(
$refField,
$negative = false,
$separator = ',',
$fieldPrefix = ''
) {
return $this->fieldFactory->create(
['fieldData' =>
[
'value' => (string) $refField,
'negative' => $negative,
'separator' => $separator
],
'fieldPrefix' => $fieldPrefix
]
);
}
/**
* Get mapping of field dependencies.
*
* @return getMappingFieldDependence[] Array of field dependencies.
*/
public function getMappingFieldDependence()
{
return [
];
}
/**
* Prepare label for tab.
*
* @return \Magento\Framework\Phrase
*/
public function getTabLabel()
{
return __('Menu Config');
}
/**
* Prepare title for tab.
*
* @return \Magento\Framework\Phrase
*/
public function getTabTitle()
{
return $this->getTabLabel();
}
/**
* {@inheritdoc}
*/
public function canShowTab()
{
return true;
}
/**
* {@inheritdoc}
*/
public function isHidden()
{
return false;
}
}
Route: app\code\Vendor\Customizablemenus\etc\adminhtml\routes.xml .
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="customizablemenus" frontName="customizablemenus">
<module name="SolideWebservices_Customizablemenus" />
</route>
</router>
</config>