Den Save-Controller richtig implementieren
Den Save-Controller richtig implementieren
~9 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Der Save-Controller ist das Gegenstück zum Form-DataProvider: Er nimmt POST-Daten entgegen, validiert sie (Kapitel 11), schreibt sie in die Datenbank und leitet je nach Ergebnis unterschiedlich weiter.
Vollständiger Save-Controller
<?php
declare(strict_types=1);
namespace Mironsoft\Announcement\Controller\Adminhtml\Announcement;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Exception\LocalizedException;
use Mironsoft\Announcement\Model\AnnouncementFactory;
use Mironsoft\Announcement\Model\ResourceModel\Announcement as AnnouncementResource;
/**
* Persists a created or edited announcement.
*/
class Save extends Action implements HttpPostActionInterface
{
public const ADMIN_RESOURCE = 'Mironsoft_Announcement::announcement';
private const PERSIST_KEY = 'mironsoft_announcement';
/**
* @param Context $context Backend action context.
* @param AnnouncementFactory $announcementFactory Factory for the announcement model.
* @param AnnouncementResource $resource Resource model for direct load/save.
* @param DataPersistorInterface $dataPersistor Keeps posted data on validation errors.
*/
public function __construct(
Context $context,
private readonly AnnouncementFactory $announcementFactory,
private readonly AnnouncementResource $resource,
private readonly DataPersistorInterface $dataPersistor,
) {
parent::__construct($context);
}
/**
* Validates and persists the posted announcement data.
*
* @return ResultInterface
*/
public function execute(): ResultInterface
{
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
$data = $this->getRequest()->getPostValue();
if (!$data) {
return $resultRedirect->setPath('mironsoft_announcement/announcement/index');
}
$id = (int) ($data['announcement_id'] ?? 0);
try {
$this->validate($data);
$announcement = $this->announcementFactory->create();
if ($id) {
$this->resource->load($announcement, $id);
}
$announcement->setData($data);
$this->resource->save($announcement);
$this->messageManager->addSuccessMessage(__('You saved the announcement.'));
$this->dataPersistor->clear(self::PERSIST_KEY);
if ($this->getRequest()->getParam('back')) {
return $resultRedirect->setPath(
'mironsoft_announcement/announcement/edit',
['id' => $announcement->getId()]
);
}
return $resultRedirect->setPath('mironsoft_announcement/announcement/index');
} catch (LocalizedException $exception) {
$this->messageManager->addErrorMessage($exception->getMessage());
} catch (\Throwable $exception) {
$this->messageManager->addErrorMessage(
__('Something went wrong while saving the announcement.')
);
}
$this->dataPersistor->set(self::PERSIST_KEY, $data);
return $resultRedirect->setPath(
'mironsoft_announcement/announcement/edit',
['id' => $id]
);
}
/**
* Validates the posted announcement data.
*
* @param array<string, mixed> $data Raw POST data.
* @return void
* @throws LocalizedException
*/
private function validate(array $data): void
{
if (trim((string) ($data['title'] ?? '')) === '') {
throw new LocalizedException(__('Title is required.'));
}
}
}HttpPostActionInterface nicht vergessen
Achtung: Ohne implements HttpPostActionInterface akzeptiert der Controller auch GET-Requests - das ist nicht nur unsauber, sondern seit den CSRF-Härtungsmaßnahmen des Magento-Admin auch ein potenzielles Sicherheitsproblem, da speichernde Aktionen zwingend an POST gebunden sein sollten.
DataPersistorInterface für Formular-Persistenz
Schlägt die Validierung fehl, gehen die eingegebenen Werte ohne DataPersistorInterface verloren - der Benutzer würde nach dem Redirect ein leeres Formular sehen und alles erneut eintippen müssen. Der Form-DataProvider muss den persistierten Wert seinerseits wieder einlesen (getData() in der DataProvider-Klasse prüft $this->dataPersistor->get() vor dem eigentlichen Collection-Load) - diese Ergänzung lohnt sich in jedem produktiven Formular.
"Speichern und weiter bearbeiten" berücksichtigen
Der Parameter back in der Redirect-Logik oben unterscheidet zwischen einem normalen Speichern (zurück zum Grid) und "Speichern und weiter bearbeiten" (zurück zum selben Formular) - dieser zweite Button wird in Kapitel 22 ergänzt, wenn eigene Toolbar-Buttons entstehen.