Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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

app/code/Mironsoft/Announcement/view/adminhtml/ui_component/mironsoft_announcement_listing.xml (excerpt)
<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:

app/code/Mironsoft/Announcement/Controller/Adminhtml/Announcement/MassDelete.php
<?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

app/code/Mironsoft/Announcement/Controller/Adminhtml/Announcement/MassStatus.php
<?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.