How to create Custom Order Grid Filters in Magento 2

Are you looking to streamline your sales order management in Magento 2? Adding custom filters to the sales order grid can greatly enhance your workflow and make it easier to find and manage orders based on specific criteria. In this tutorial, we'll walk you through the process of adding a custom filter for order location to the sales order grid in Magento 2.

Step 1: Create a Custom Magento 2 Module

First things first, make sure you have your custom module set up. If not, create one to house our custom filter functionality.

Step 2: Define the Filter

Next, we'll add a filter option for order location to the sales order grid. Create a sales_order_grid.xml file in your module's view/adminhtml/ui_component/ directory and insert the necessary code to define the filter.

<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <listingToolbar name="listing_top">
        <filters name="listing_filters">
            <filterSelect name="order_locations" provider="${ $.parentName }">
                <settings>
                  <options class="MageMastery\OrderGridFilters\Ui\Component\Listing\Column\Order\Locations"/>
                    <label translate="true">Order Location</label>
                    <dataScope>order_locations</dataScope>
                </settings>
            </filterSelect>
        </filters>
    </listingToolbar>
</listing>

Step 3: Provide Filter Options

Now, let's provide the options for our custom filter. Create a Locations file in your module's Ui/Component/Listing/Column/Order/ directory and populate it with the code to specify the filter options.

declare(strict_types=1);

namespace MageMastery\OrderGridFilters\Ui\Component\Listing\Column\Order;

use Magento\Framework\Data\OptionSourceInterface;

class Locations implements OptionSourceInterface
{
    public const INTERNATIONAL_ORDERS = 'international_orders';
    public const EUROPEAN_ORDERS = 'eu_orders';

    /**
     * @var array
     */
    private array $options;

    /**
     * Prepare options array for the filter
     *
     * @return array
     */
    public function toOptionArray(): array
    {
        if (!empty($this->options)) {
            return $this->options;
        }

        $this->options = [
            [
                'label' => ' ',
                'value' => '',
            ],
            [
                'label' => 'International',
                'value' => self::INTERNATIONAL_ORDERS,
            ],
            [
                'label' => 'Europe',
                'value' => self::EUROPEAN_ORDERS,
            ]
        ];

        return $this->options;
    }
}

Step 4: Implement a Plugin for Filter Functionality

To make our custom filter functional, we'll need to add a plugin to apply our filter on the grid. Create a di.xml file in your module's etc/adminhtml/ directory and configure it with the necessary code to define the plugin.

xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
      <plugin name="orderGridFilterPlugin" type="MageMastery\OrderGridFilters\Plugin\Model\ResourceModel\Order\OrderGridFilter"/>
    </type>
</config>

Step 5: Apply the Filter Logic

Finally, let's write the code to apply our custom filter on the sales order grid. Implement the logic to check if the applied filter is ours and process the business logic accordingly.

declare(strict_types=1);

namespace MageMastery\OrderGridFilters\Plugin\Model\ResourceModel\Order;

use Closure;
use Magento\Framework\DB\Select;
use Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult;
use MageMastery\OrderGridFilters\Service\CountriesService;

class OrderGridFilter
{
    private const INTERNATIONAL_ORDERS = 'international_orders';
    private const EUROPEAN_ORDERS = 'eu_orders';

    /**
     * @param CountriesService $getCountriesService
     */
    public function __construct(
        private readonly CountriesService $getCountriesService
    ) {
    }

    /**
     * Conditional column filters with timezone convertor interface
     *
     * @param SearchResult $subject
     * @param Closure $proceed
     * @param string $field
     * @param array|null $condition
     * @return SearchResult|mixed
     */
    public function aroundAddFieldToFilter(
        SearchResult $subject,
        Closure      $proceed,
        $field,
        $condition = null
    ): mixed {
        if ($field === 'order_locations') {
            if (!empty($condition)) {
                if (in_array(self::INTERNATIONAL_ORDERS, $condition)) {
                    $condition = ['nin' => $this->getCountriesService->getListOfExcludedCountries()];
                } elseif (in_array(self::EUROPEAN_ORDERS, $condition)) {
                    $condition = ['in' => $this->getCountriesService->getEUCountriesList()];
                }
                $condition = $subject->getConnection()->prepareSqlCondition('soa.country_id', $condition);
                $subject->getSelect()->where($condition, null, Select::TYPE_CONDITION);
            }

            return $subject;
        }

        return $proceed($field, $condition);
    }
}

Finally, let's write the code to apply our custom filter on the sales order grid. Implement the logic to check if the applied filter is ours and process the business logic accordingly.

declare(strict_types=1);

namespace MageMastery\OrderGridFilters\Plugin\Model\ResourceModel\Order;
use Closure; use Magento\Framework\DB\Select; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult; use MageMastery\OrderGridFilters\Service\CountriesService; class OrderGridFilter { private const INTERNATIONAL_ORDERS = 'international_orders'; private const EUROPEAN_ORDERS = 'eu_orders'; /** * @var CountriesService */ private CountriesService $getCountriesService; /** * @param CountriesService $getCountriesService */ public function __construct( CountriesService $getCountriesService ) { $this->getCountriesService = $getCountriesService; } /** * Conditional column filters with timezone convertor interface * * @param SearchResult $subject * @param Closure $proceed * @param string $field * @param array|null $condition * @return SearchResult|mixed */ public function aroundAddFieldToFilter( SearchResult $subject, Closure $proceed, $field, $condition = null ): mixed { if ($field === 'order_locations') { if (!empty($condition)) { if (in_array(self::INTERNATIONAL_ORDERS, $condition)) { $condition = ['nin' => $this->getCountriesService->getEUCountriesList()]; } elseif (in_array(self::EUROPEAN_ORDERS, $condition)) { $condition = ['in' => $this->getCountriesService->getEUCountriesList()]; } $condition = $subject->getConnection()->prepareSqlCondition('soa.country_id', $condition); $resource = $subject->getResource(); $subject->getSelect()->joinLeft( ['soa' => $resource->getTable('sales_order_address')], 'soa.parent_id = main_table.entity_id AND soa.address_type = \'shipping\'', ['country_id'] ); $subject->getSelect()->where($condition, null, Select::TYPE_CONDITION); } return $subject; } return $proceed($field, $condition); } }

With these steps completed, you'll have successfully added your custom filter to the sales order grid in Magento 2. Enjoy the enhanced order management capabilities and streamline your workflow like never before!