TransWikia.com

Product attribute in filter

Magento Asked on December 14, 2021

My issue is quite simple to resume :

I have 2 products attribute. One of them is already appearing in my filter front panel. The other is not.

I have almost the same configuration for both of them.
What’s tilting me is that I think the configuration is kind of ignored (indeed with that configuration I don’t think neither of these attributes should be added to the filter panel) but one is.

As you can see here is what I have under Product Attributes

product attributes

The first one is already appearing in filter, but not the second one.

front filter

Inside their configuration everything is the same

So my issue is : Why the second one isn’t appearing (I have enought data to make it appear, so it’s not because I don’t have any products with that attributes).
Is there some place in the code where I can check what is going on ?

First attributes properties (the second one is the exact same except he has system values).

properties part 1

properties part 2

properties storefront

Attributes creation :

$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);  

$eavSetup->addAttribute(
        MagentoCatalogModelProduct::ENTITY, 'longueur_tasse_vertuo', [
            'type'  => 'text',
            'backend' => 'MagentoEavModelEntityAttributeBackendArrayBackend',
            'frontend' => '',
            'label' => 'Longueur de tasse Vertuo',
            'input' => 'multiselect',
            'class' => '',
            'source' => 'CpyCatalogModelConfigProductCupSizeVertuooption',
            'global' => MagentoEavModelEntityAttributeScopedAttributeInterface::SCOPE_STORE,
            'visible' => true,
            'required' => false,
            'group' => '',
            'sort_order' => 203,
            'searchable' => false,
            'filterable' => true,
            'comparable' => false,
            'used_in_product_listing' => true,
            'visible_on_front' => true
        ]
    );

The sources file defines (which are the default system values.

public function getAllOptions()
{
    $this->_options = [];
    $this->_options[] = ['label' => 'Espresso', 'value' => '1'];
    $this->_options[] = ['label' => 'Double Espresso', 'value' => '2'];
    $this->_options[] = ['label' => 'Gran Lungo', 'value' => '3'];
    $this->_options[] = ['label' => 'Mug', 'value' => '4'];
    $this->_options[] = ['label' => 'Alto', 'value' => '5'];

    return $this->_options;
}

I can’t find any setup for the first one that is already appearing.

Also if It can help I have this in a ‘product_cofee.xml’ layout

<block class="MagentoCatalogBlockProductViewDescription" name="product.info.cafe.cupsize" template="Magento_Catalog::product/view/cafe/cup-size.phtml" before="product.info.price" >
    <arguments>
        <argument name="at_call" xsi:type="string">getLongueurTasse</argument>
        <argument name="at_code" xsi:type="string">longueur_tasse</argument>
        <argument name="at_type" xsi:type="string">text</argument>
        <argument name="css_class" xsi:type="string">product-nespresso-cupsize</argument>
        <argument name="at_label" xsi:type="string">Taille des tasses :</argument>
        <argument name="add_attribute" xsi:type="string">itemprop="nespresso-cupsize"</argument>
    </arguments>
</block>

<block class="MagentoCatalogBlockProductViewDescription" name="product.info.cafe.cupsize.vertuo" template="Magento_Catalog::product/view/cafe/vertuo/cup-size.phtml" before="product.info.price" >
    <arguments>
        <argument name="at_call" xsi:type="string">getLongueurTasseVertuo</argument>
        <argument name="at_code" xsi:type="string">longueur_tasse_vertuo</argument>
        <argument name="css_class" xsi:type="string">product-nespresso-cupsize-vertuo</argument>
        <argument name="at_label" xsi:type="string">Taille des tasses :</argument>
        <argument name="add_attribute" xsi:type="string">itemprop="nespresso-cupsize"</argument>
    </arguments>
</block>

#EDIT

My query

2020-07-24T08:37:17+00:00 INFO (6): SELECT `main_table`.`entity_type_id`, `main_table`.`attribute_code`, `main_table`.`attribute_model`, `main_table`.`backend_model`, `main_table`.`backend_type`, `main_table`.`backend_table`, `main_table`.`frontend_model`, `main_table`.`frontend_input`, `main_table`.`frontend_label`, `main_table`.`frontend_class`, `main_table`.`source_model`, `main_table`.`is_required`, `main_table`.`is_user_defined`, `main_table`.`default_value`, `main_table`.`is_unique`, `main_table`.`note`, `additional_table`.*, IFNULL(al.value, main_table.frontend_label) AS `store_label` FROM `eav_attribute` AS `main_table`
 INNER JOIN `catalog_eav_attribute` AS `additional_table` ON additional_table.attribute_id = main_table.attribute_id
 LEFT JOIN `eav_attribute_label` AS `al` ON al.attribute_id = main_table.attribute_id AND al.store_id = 7 WHERE (main_table.entity_type_id = 4) AND (`additional_table`.`is_filterable` > 0) ORDER BY position ASC

#EDIT Add second request

2020-07-24T13:19:40+00:00 INFO (6): longueur_tasse
2020-07-24T13:19:40+00:00 INFO (6): SELECT `e`.*, `cat_index`.`position` AS `cat_index_position`, `price_index`.`price`, `price_index`.`tax_class_id`, `price_index`.`final_price`, IF(price_index.tier_price IS NOT NULL, LEAST(price_index.min_price, price_index.tier_price), price_index.min_price) AS `minimal_price`, `price_index`.`min_price`, `price_index`.`max_price`, `price_index`.`tier_price`, `stock_status_index`.`is_salable` FROM `catalog_product_entity` AS `e`
 INNER JOIN `catalog_category_product_index_store7` AS `cat_index` ON cat_index.product_id=e.entity_id AND cat_index.store_id=7 AND cat_index.visibility IN(2, 4) AND cat_index.category_id=43
 INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.website_id = '8' AND price_index.customer_group_id = 0
 INNER JOIN `catalog_product_entity` AS `product` ON product.entity_id = e.entity_id
 INNER JOIN `inventory_stock_8` AS `stock_status_index` ON product.sku = stock_status_index.sku
 INNER JOIN `search_tmp_5f1adfebb39791_19171320` AS `search_result` ON e.entity_id = search_result.entity_id ORDER BY `cat_index`.`position` asc, `e`.`entity_id` DESC, `cat_index`.`position` asc
 LIMIT 10

2020-07-24T13:19:40+00:00 INFO (6): longueur_tasse_vertuo
2020-07-24T13:19:40+00:00 INFO (6): SELECT `e`.*, `cat_index`.`position` AS `cat_index_position`, `price_index`.`price`, `price_index`.`tax_class_id`, `price_index`.`final_price`, IF(price_index.tier_price IS NOT NULL, LEAST(price_index.min_price, price_index.tier_price), price_index.min_price) AS `minimal_price`, `price_index`.`min_price`, `price_index`.`max_price`, `price_index`.`tier_price`, `stock_status_index`.`is_salable` FROM `catalog_product_entity` AS `e`
 INNER JOIN `catalog_category_product_index_store7` AS `cat_index` ON cat_index.product_id=e.entity_id AND cat_index.store_id=7 AND cat_index.visibility IN(2, 4) AND cat_index.category_id=43
 INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.website_id = '8' AND price_index.customer_group_id = 0
 INNER JOIN `catalog_product_entity` AS `product` ON product.entity_id = e.entity_id
 INNER JOIN `inventory_stock_8` AS `stock_status_index` ON product.sku = stock_status_index.sku
 INNER JOIN `search_tmp_5f1adfebb39791_19171320` AS `search_result` ON e.entity_id = search_result.entity_id ORDER BY `cat_index`.`position` asc, `e`.`entity_id` DESC, `cat_index`.`position` asc
 LIMIT 10

EDIT 18/08 :

I’ve been finding the class responsible for the missing data
It’s MagentoFrameworkSearchAdapterMysqlAggregationBuilder

And here is the method

public function build(
    DataProviderInterface $dataProvider,
    array $dimensions,
    RequestBucketInterface $bucket,
    Table $entityIdsTable
) {
    $metrics = $this->metricsBuilder->build($bucket);

    $select = $dataProvider->getDataSet($bucket, $dimensions, $entityIdsTable);
    $select->columns($metrics);
    $select->group(RequestBucketInterface::FIELD_VALUE);

    if($bucket->getName() === 'longueur_tasse_vertuo_bucket' || $bucket->getName() === 'longueur_tasse_bucket'){
        var_dump($bucket->getName());
        var_dump($dataProvider->execute($select));
    }
    return $dataProvider->execute($select);
}

With longueur_tasse_bucket I’ll have the result while with longueur_tasse_vertuo_bucket I won’t have anything

3 Answers

The main issue belongs to the class vendor/magento/module-catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php

The method

protected function _getIndexableAttributes($multiSelect)
{
    $select = $this->getConnection()->select()->from(
        ['ca' => $this->getTable('catalog_eav_attribute')],
        'attribute_id'
    )->join(
        ['ea' => $this->getTable('eav_attribute')],
        'ca.attribute_id = ea.attribute_id',
        []
    )->where(
        $this->_getIndexableAttributesCondition()
    );

    if ($multiSelect == true) {
        $select->where('ea.backend_type = ?', 'varchar')->where('ea.frontend_input = ?', 'multiselect');
    } else {
        $select->where('ea.backend_type = ?', 'int')->where('ea.frontend_input IN( ? )', ['select', 'boolean']);
    }
    return $this->getConnection()->fetchCol($select);
}

For multiselect, the required type was varchar while my attribute had a type text.

So all we have to do is to change the attribute type to varchar and then play the indexer once again to get the association geing written in the table catalog_product_index_eav making it possible to be appearing in the filter.

This is for the quick and immediate fix. In the same time, I send the issue to magento to make this multiselect where condition be able to read both text and varchar type, feel free to check it : https://github.com/magento/magento2/issues/29676

Answered by Claims on December 14, 2021

You need to put some log at the function MagentoCatalogModelLayerCategoryFilterableAttributeList::getList

this is temporary and therefore can be in the vendor folder directly.

The log we want to use is to know what query runs when the layer filters are pulled in the block MagentoLayeredNavigationBlockNavigation

put the lines below just above the line $collection->load();

$writer = new ZendLogWriterStream(BP . '/var/log/layerdebug.log');
$logger = new ZendLogLogger();
$logger->addWriter($writer);
$logger->info($collection->getSelect()->__toString());

My query on my system is:

SELECT `main_table`.`entity_type_id`, `main_table`.`attribute_code`, `main_table`.`attribute_model`, `main_table`.`backend_model`, `main_table`.`backend_type`, `main_table`.`backend_table`, `main_table`.`frontend_model`, `main_table`.`frontend_input`, `main_table`.`frontend_label`, `main_table`.`frontend_class`, `main_table`.`source_model`, `main_table`.`is_required`, `main_table`.`is_user_defined`, `main_table`.`default_value`, `main_table`.`is_unique`, `main_table`.`note`, `additional_table`.*, IFNULL(al.value, main_table.frontend_label) AS `store_label` FROM `eav_attribute` AS `main_table`
 INNER JOIN `catalog_eav_attribute` AS `additional_table` ON additional_table.attribute_id = main_table.attribute_id
 LEFT JOIN `eav_attribute_label` AS `al` ON al.attribute_id = main_table.attribute_id AND al.store_id = 1 WHERE (main_table.entity_type_id = 4) AND (`additional_table`.`is_filterable` > 0)

Once we will know your query, it is likely we will understand why your first system product attribute is not with us.

Step 2: if the above returns the attribute, at this point we want to check if the attributes has some options to render:

same process as above can apply to troubleshoot, you may go to the function MagentoCatalogSearchModelLayerFilterAttribute::_getItemsData

and put the log just above this line

$isAttributeFilterable =
            $this->getAttributeIsFilterable($attribute) === static::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS;



$writer = new ZendLogWriterStream(BP . '/var/log/layerdebug.log');
    $logger = new ZendLogLogger();
    $logger->addWriter($writer);
    $logger->info($productCollection->getSelect()->__toString());

then at this point, everything should come together. Of course, the query may be big but essentially, you are within the bone of the feature.. and then there is nothing deeper.. so we better have to find from here

Answered by Herve Tribouilloy on December 14, 2021

It does seem the one that is product attribute that is system does not want to be in the layer.

I will look into it now. Having said that, one would think it might be worth creating a new product attribute identical to the no system one and then 'do assign your data to the new attribute and refresh index..'

that should be a quick win, if you want to do this and you miss a script to update the attribute, I surely can provide you with this script

Answered by Herve Tribouilloy on December 14, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP