Mass Actions: Activate/Deactivate, Delete
Mass Actions: Activate/Deactivate, Delete
~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026
Mass actions let you operate on several selected rows at once - typically deleting or toggling a status. On the JavaScript side, nothing more is needed than a declaration in listing.xml; on the PHP side, every action needs its own controller.
Declaring massaction in listing.xml
<massaction name="listing_massaction">
<action name="delete">
<settings>
<confirm>
<title translate="true">Delete</title>
<message translate="true">Are you sure you want to delete selected announcements?</message>
</confirm>
<url path="mironsoft_announcement/announcement/massDelete"/>
<type>delete</type>
<label translate="true">Delete</label>
</settings>
</action>
<action name="activate">
<settings>
<url path="mironsoft_announcement/announcement/massStatus">
<param name="status" xsi:type="number">1</param>
</url>
<type>activate</type>
<label translate="true">Activate</label>
</settings>
</action>
<action name="deactivate">
<settings>
<url path="mironsoft_announcement/announcement/massStatus">
<param name="status" xsi:type="number">0</param>
</url>
<type>deactivate</type>
<label translate="true">Deactivate</label>
</settings>
</action>
</massaction>Each <action> only needs a url, an optional confirm for a confirmation dialog, and optional static param values sent along with every call (here: the target status).
Magento\Ui\Component\MassAction\Filter
The controller doesn't have to read and filter the IDs sent by the frontend itself - that's what Filter is for, returning an already-filtered collection directly:
<?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\Controller\ResultFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Ui\Component\MassAction\Filter;
use Mironsoft\Announcement\Model\ResourceModel\Announcement\CollectionFactory;
/**
* Deletes all announcements selected via a grid mass action.
*/
class MassDelete extends Action
{
public const ADMIN_RESOURCE = 'Mironsoft_Announcement::announcement';
/**
* @param Context $context Backend action context.
* @param Filter $filter Applies the grid selection onto a collection.
* @param CollectionFactory $collectionFactory Factory for the announcement collection.
*/
public function __construct(
Context $context,
private readonly Filter $filter,
private readonly CollectionFactory $collectionFactory,
) {
parent::__construct($context);
}
/**
* Deletes the selected announcements and redirects back to the grid.
*
* @return ResultInterface
* @throws LocalizedException
*/
public function execute(): ResultInterface
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$deleted = 0;
foreach ($collection as $announcement) {
$announcement->delete();
$deleted++;
}
$this->messageManager->addSuccessMessage(
__('A total of %1 record(s) have been deleted.', $deleted)
);
/** @var ResultInterface $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('mironsoft_announcement/announcement/index');
}
}$this->filter->getCollection() automatically handles both cases: either an explicit list of selected IDs, or "all rows matching the current filter" (when the user picks "Select All") - both cases are correctly covered without any extra code.
MassStatus for activate/deactivate
<?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\Controller\ResultFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Ui\Component\MassAction\Filter;
use Mironsoft\Announcement\Model\ResourceModel\Announcement\CollectionFactory;
/**
* Toggles the active status of the announcements selected via a mass action.
*/
class MassStatus extends Action
{
public const ADMIN_RESOURCE = 'Mironsoft_Announcement::announcement';
/**
* @param Context $context Backend action context.
* @param Filter $filter Applies the grid selection onto a collection.
* @param CollectionFactory $collectionFactory Factory for the announcement collection.
*/
public function __construct(
Context $context,
private readonly Filter $filter,
private readonly CollectionFactory $collectionFactory,
) {
parent::__construct($context);
}
/**
* Applies the requested status to the selected announcements.
*
* @return ResultInterface
*/
public function execute(): ResultInterface
{
$status = (int) $this->getRequest()->getParam('status');
$collection = $this->filter->getCollection($this->collectionFactory->create());
foreach ($collection as $announcement) {
$announcement->setIsActive($status)->save();
}
$this->messageManager->addSuccessMessage(__('Status updated.'));
/** @var ResultInterface $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('mironsoft_announcement/announcement/index');
}
}Achtung: Mass actions run against the database without any separate confirmation if no <confirm> is set in the XML - for destructive actions like delete, a confirmation dialog is therefore mandatory, not just a nice-to-have.