0

I've made a custom multiselect customer attribute. The attribute code is: email_preference. I've added this code to get a multiselect filter in the app/code/Vendor/Customer/view/adminhtml/ui_component/customer_listing.xml:

<?xml version="1.0" encoding="UTF-8"?>

<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">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="templates" xsi:type="array">
                        <item name="filters" xsi:type="array">
                            <item name="select" xsi:type="array">
                                <item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
                                <item name="template" xsi:type="string">ui/grid/filters/elements/ui-select</item>
                            </item>
                        </item>
                    </item>
                </item>
            </argument>
        </filters>
    </listingToolbar>
    <columns name="customer_columns" class="Magento\Customer\Ui\Component\Listing\Columns">
        <column name="email_preference">
            <argument name="data" xsi:type="array">
                <item name="options" xsi:type="object">\Vendor\Customer\Model\Customer\Attribute\Source\EmailPreference</item>
                <item name="config" xsi:type="array">
                    <item name="filter" xsi:type="string">select</item>
                    <item name="dataType" xsi:type="string">select</item>
                    <item name="label" xsi:type="string" translate="true">Email preferences</item>
                    <item name="sortOrder" xsi:type="number">300</item>
                </item>
            </argument>
        </column>
    </columns>
</listing>

In the \Vendor\Customer\Model\Customer\Attribute\Source\EmailPreference I have:

use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;

class EmailPreference extends AbstractSource
{
    /**
     * @return array
     */
    public function getAllOptions(): array
    {
        $this->_options = [];
        $this->_options[] = ['label' => 'Value1', 'value' => 'value1'];
        $this->_options[] = ['label' => 'Value2, 'value' => 'value2'];

        return $this->_options;
    }
}

This filter works only if I have one value assigned to a customer. If I have multi values assinged, it will render in the grid like this: Value1,Value2, however If I select Value1 and Value2 from the filter , I will only get customers with one values assigned. Either Value1 or Value2 , but not Value1,Value. For example, in this case, if I select Value1 and Value2 from the multiselect:

Customer 1 -> Value1
Customer 2 -> Value2
Customer 3 -> Value1,Value2

I will get only Customer 1 and Customer 2, not Customer 3. How can I change my filter to make this work ? or What am I missing ?

Thanks a lot!

2
  • try making your column multiselect instead of select <item name="dataType" xsi:type="string">multiselect</item>. Not sure if it works, but it's easy to test
    – Marius
    Feb 3, 2022 at 9:34
  • thanks for suggestion @Marius, but it does not work . I followed this : magento.stackexchange.com/a/186608/6204 Feb 3, 2022 at 9:40

1 Answer 1

0

I think I've got something. Here is what happens (I think).

When you apply a filter on the customer email_preference field this is actually applied to the collection query. (Let's imagine that the customer entity is flat even if it's EAV. it's easier to explain.)

WHERE email_preference IN ('value1', 'value2');`  

but since in your database you have saved like a string value1,value2 this is never going to get matched.

What may possible work is to make the filter look like this

WHERE FIND_IN_SET('value1', email_preference) AND FIND_IN_SET ('value2', 'email_preference')

This is a part of the stack trace where you may be able to hook to do this.

  • Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider::getSearchResult
  • Magento\Framework\View\Element\UiComponent\DataProvider\Reporting::search
  • Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool::applyFilters
  • Magento\Framework\View\Element\UiComponent\DataProvider::apply
  • Magento\Customer\Model\ResourceModel\Grid\Collection::addFieldToFilter

I would go with an aroundPlugin (I know, ugly, but I have no better idea for now) on Magento\Framework\View\Element\UiComponent\DataProvider::apply

where you can do something like this

public function aroundApply(
    \Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter $subject,
     callable $proceed;
     \Magento\Framework\Data\Collection $collection,
     \Magento\Framework\Api\Filter $filter
)
{
    if ($collection instanceof \Magento\Customer\Model\ResourceModel\Grid\Collection::class && $filter->getField() === 'email_preference') {
        $values = !is_array($filter->getValue()) ? explode(',', $filter->getValue()) : $filter->getValue();
        foreach ($values as $item) {
            $collection->addFieldToFilter($filter->getField(), ['finset' => $item]);
        }
    } else {
       $proceed($collection, $filter);
    }
}

Haven't tested for an EAV entity like "customer" but it looks like it might work for a flat one. but the filter appliers should be entity type agnostic.

If you want OR instead of AND for the filter, you need to adjust the way you add the filters but the idea should be the same.

you can also try with a plugin on addFieldToFilter on the Magento\Customer\Model\ResourceModel\Grid\Collection class, but this looks trickier

1
  • Thanks Marius , This could actually work, it is a good approach :) Feb 3, 2022 at 13:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.