We sacrifice by not doing any other technology, so that you get the best of Magento.

We sacrifice by not doing any other technology, so that you get the best of Magento.

    How to set layered navigation filters position category wise in Magento 2?

    By default, Magento adjusts the attribute position when we create product attributes. But, systematically, the same position will get apply for all categories.

    So if we wanted to display attributes position differently for categories wise, then this article is your solution.

    First, you have to create text type Category Attributes
    which you need to set differently category wise (Please verify product attribute code and category attribute code).

    In the text type attributes, you have to set the require position value as shown in below screenshot.

    After creating all the category attributes and setting value in all categories, you have to move forword in the next step:

    Override the MagentoCatalogModelLayerCategoryFilterableAttributeList file.

    For that, you need to use virtualType in di.xml in your module (You can create a new module or use any existing module. Create only di.xml in that module).

    <virtualType name="categoryFilterList" type="[Vendor Name][Module Name]ModelLayerFilterList">
        <arguments>
            <argument name="filterableAttributes" xsi:type="object">MagentoCatalogModelLayerCategoryFilterableAttributeList</argument>
        </arguments>
    </virtualType>

    After override FilterList, put below code in that file for change the Filter Position:

    <?php
    namespace [Vendor Name][Module Name]ModelLayer;
    
    /**
     * Override FilterList Class
     */
    class FilterList extends MagentoCatalogModelLayerFilterList {
    
        protected $_categoryFactory;
    
        public function __construct(MagentoCatalogModelCategoryFactory $categoryFactory, MagentoFrameworkViewElementTemplateContext $context, MagentoFrameworkRegistry $registry) {
            $this->_registry = $registry;
            $this->_categoryFactory = $categoryFactory;
            parent::__construct($context);
        }
    
        public function getFilters(MagentoCatalogModelLayer $layer) {
            $arr = array();
            $new = array();
            $value = array();
            if (!count($this->filters)) {
                $this->filters = [$this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]), ];
                $i = 1;
                $arraycheck = array();
                $inarraycheck = array();
                $valuesCol = array();
                $allready = array();
                $objectManager = MagentoFrameworkAppObjectManager::getInstance();
                $currentcategory = $this->_registry->registry('current_category');
                $categoryId = $currentcategory->getId();
                $categoryData = $this->_categoryFactory->create()->load($categoryId);
                $getData = $categoryData->getData();
                foreach ($getData as $key => $value) {
                    $new[] = $key;
                }
                foreach ($this->filterableAttributes->getList() as $attribute) {
                    $code = $attribute->getAttributeCode();
                    if (in_array($code, $new)) {
                        if (!empty($getData[$code])) {
                            $valuesCol[] = $posVal = $getData[$code];
                        }
                    }
                }
                foreach ($this->filterableAttributes->getList() as $attribute) {
                    $code = $attribute->getAttributeCode();
                    if (in_array($code, $new)) {
                        if (!empty($getData[$code])) {
                            $posVal = $getData[$code];
                            array_push($inarraycheck, $attribute->getAttributeCode());
                            if (array_key_exists($posVal, $this->filters)) {
                                $arraycheck[] = $this->createAttributeFilter($attribute, $layer);
                                $this->filters[$posVal] = $this->createAttributeFilter($attribute, $layer);
                            } else {
                                $this->filters[$posVal] = $this->createAttributeFilter($attribute, $layer);
                            }
                        } else {
                            $this->filters[$i] = $this->createAttributeFilter($attribute, $layer);
                        }
                    } else {
                        if (in_array($i, $valuesCol)) {
                            $allready[] = $this->createAttributeFilter($attribute, $layer);
                        } else {
                            $this->filters[$i] = $this->createAttributeFilter($attribute, $layer);
                        }
                    }
                    $i++;
                    if (!empty($arraycheck)) {
                        foreach ($arraycheck as $key => $value) {
                            if (!in_array($attribute->getAttributeCode(), $inarraycheck)) {
                                $this->filters[$i] = $value;
                            }
                            $i++;
                            $arraycheck = array();
                        }
                    }
                    if (!empty($allready)) {
                        foreach ($allready as $key1 => $value1) {
                            $this->filters[$i] = $value1;
                        }
                        $i++;
                        $allready = array();
                    }
                }
                ksort($this->filters);
            }
            return $this->filters;
        }
    }
    ?>

    After doing all the steps run below commands and check output on front end.

    You will see layered navigation filters are displayed differently in each category.

    php bin/magento setup:di:compile
    php bin/magento cache:flush

     

    Magento 2.3.x Attributes options not created correctly programmatically

    If you want to create product attribute options programatically, but not by admin then this blog post is right the solution for you.

    You need to add below code in controller.

    Codes for 2.2.x & 2.3.x will be different.

    Code for Magento 2.2.X :

    $optionLabel = $this->optionLabelFactory->create();
    $optionLabel->setStoreId(0);
    $optionLabel->setLabel($attoption);
    
    $option = $this->optionFactory->create();
    $option->setLabel($optionLabel);
    $option->setStoreLabels([$optionLabel]);
    $option->setSortOrder(0);
    $option->setIsDefault(false);
    
    $this->attributeOptionManagement->add(MagentoCatalogModelProduct::ENTITY,
    $this->attributeRepository->get($attrname)->getAttributeId(),$option);
    
    $_product = $this->productFactory->create();
    $isAttributeExist = $_product->getResource()->getAttribute($attrname);
    $optionId = '';
    if ($isAttributeExist && $isAttributeExist->usesSource()) {
        $optionId = $isAttributeExist->getSource()->getOptionId($attoption);
    }
    
    return $optionId;

    Code for Magento 2.3.X

    $optionLabel = $this->optionLabelFactory->create();
    $optionLabel->setStoreId(0);
    $optionLabel->setLabel($attoption);
    
    $option = $this->optionFactory->create();
    $option->setLabel($attoption);
    $option->setStoreLabels([$optionLabel]);
    $option->setSortOrder(0);
    $option->setIsDefault(false);
    
    $this->attributeOptionManagement->add(MagentoCatalogModelProduct::ENTITY,
    $this->attributeRepository->get($attrname)->getAttributeId(),$option);
    
    $_product = $this->productFactory->create();
    $isAttributeExist = $_product->getResource()->getAttribute($attrname);
    $optionId = '';
    if ($isAttributeExist && $isAttributeExist->usesSource()) {
        $optionId = $isAttributeExist->getSource()->getOptionId($attoption);
    }
    
    return $optionId;

    I hope adding the above codes in their respective versions of your Magento store will help you to create product attribute options. Let us know by comment if it worked for you or not?

    Get layered navigation attributes list Magento 2

    Get all the attributes which are used in layered navigation in category & search page.

    You can get all the filterable attribute list from MagentoCatalogModelResourceModelProductAttributeCollectionFactory class.

    As a result, you will get that,

    all those attributes which have been used in Layered Navigation status are Filterable (with results) or Filterable (no results).

    So, you need to create __construct( ) function for define dependency of Class.

    <?php
        private $checkoutSession;
    
        public function __construct(
            MagentoCatalogModelResourceModelProductAttributeCollectionFactory $productAttributeCollectionFactory
        ) {
            $this->productAttributeCollectionFactory = $productAttributeCollectionFactory;
        }
    
        public function getFilterableAttributes()
        {
            /** @var MagentoCatalogModelResourceModelProductAttributeCollection $productAttributes */
            $productAttributes = $this->productAttributeCollectionFactory->create();
            $productAttributes->addFieldToFilter(
                ['is_filterable', 'is_filterable_in_search'],
                [[1, 2], 1]
            );
    
            return $productAttributes;
        }

    Now call a function in template file by iterating over a loop to fetch each attribute code,

    $getAttributes = $block->getFilterableAttributes();
    
        foreach ($getAttributes as $attribute) {
            echo $attribute->getAttributeCode();echo "<br>";
        }

    You can get all the filterable attribute used for your store.

    The Result

        price
        manufacturer
        color
        activity
        style_bags
        material
        strap_bags
        features_bags
        gender
        category_gear
        size
        eco_collection
        performance_fabric
        erin_recommends
        new
        sale
        format
        style_bottom
        style_general
        sleeve
        collar
        pattern
        climate

     

    The XML invalid Element indexer, attribute class: [facet ‘pattern’] The value in developer mode

    Please Follow Below steps to resolved such error of Digit Pattern :

    Making below changes on given files will help you to solve the error.

    File Path is : /vendor/magento/framework/Indexer/etc/indexer.xsd

    Do below change in line number 74:

    <xs:pattern value="[a-zA-Z\]+" /> to <xs:pattern value="[a-zA-Z0-9\]+" />

    File Path is : /vendor/magento/framework/Indexer/etc/indexer_merged.xsd

    Do below change in line number 64:

    <xs:pattern value="[a-zA-Z\]+" /> to <xs:pattern value="[a-zA-Z0-9\]+" />

    File Path is : /vendor/magento/framework/Mview/etc/mview.xsd

    Do below change in line number Line58:

    <xs:pattern value="[a-zA-Z\]+" /> to <xs:pattern value="[a-zA-Z0-9\]+" />

    Clean the cache and rerun the program.

    Let us know via if this tutorial helps you to overcome the problem or not?

    How to add some logic when product added from backend in Magento?

    We will achieve this functionality through EventObserver.

    Here, I have used the event which is called

    “controller_action_catalog_product_save_entity_after”

    Step 1: Create event.xml in /etc/adminhtml folder in your module.

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
        <event name="controller_action_catalog_product_save_entity_after">
            <observer name="magemonkeys_saveproductlogic" instance="MagemonkeysSaveproductlogicObserverSaveProduct" />
        </event>
    </config>
    

    Step 2: Create a SaveProduct.php Observer in Observer Folder.

    <?php
    namespace MagemonkeysSaveproductlogicObserver;
    
    use MagentoFrameworkEventObserverInterface;
    
    class SaveProduct implements ObserverInterface
    {
        protected $catalogData;
    
        protected $_resource;
    
        
        public function __construct(
            MagentoFrameworkAppResourceConnection $resource
            )
        {
            $this->_resource = $resource;
        }
    
        
        public function execute(MagentoFrameworkEventObserver $observer)
        {
            $connection = $this->_resource->getConnection();
            $table_name = $this->_resource->getTableName('product_oem');
            $productController = $observer->getController();
            $data = $productController->getRequest()->getPostValue();
    
            $mainProduct = $observer->getProduct();
            $productId = $mainProduct->getId();
            /* You can use you logic here */
    
            if(isset($data['product']['product_oem']) && $productId){
                $connection->query('DELETE FROM ' . $table_name . ' WHERE product_id =  ' . (int)$productId . ' ');
                $productOems = $data['product']['product_oem'];
                if(!is_array($productOems)){
                    $productOems = array();
                    $productOems[] = (int)$data['product']['product_oem'];
                }
                foreach ($productOems as $k => $v) {
                    $connection->query('INSERT INTO ' . $table_name . ' VALUES ( ' . $v . ', ' . (int)$productId . ',0)');
                }
            }
        }
    }
    ?>

    After creating the above module, please run the upgrade, compile and static:content:deploy command to implement this module.

    Overriding MagentoCatalogModelLayer Model class in Magento 2

    To customize the layered navigation in Magento 2, sometimes, we need to override the core class file. Generally, we can use preference to override models, but it’s not possible every time.

    So on that case, we need to use a different approach and have to use virtualType to achieve that.

    Please find below the steps to override models using virtualType.

    1. Create an di.xml in : /app/code/Magemonkeys/LayerModel/etc

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <virtualType name="categoryFilterList" type="MagemonkeysLayerModelModelLayerFilterList">
          <arguments>
             <argument name="filterableAttributes" xsi:type="object">MagentoCatalogModelLayerCategoryFilterableAttributeList</argument>
          </arguments>
        </virtualType> 
    </config>

    2. Create an FilterList.php in : /app/code/Magemonkeys/LayerModel/Model/Layer

    <?php
     namespace MagemonkeysLayerModelModelLayer;
     class FilterList extends MagentoCatalogModelLayerFilterList
     {
         public function getFilters(MagentoCatalogModelLayer $layer)
         { 
           if (!count($this->filters)) {
              $this->filters = [
                 $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]),
              ];
              foreach ($this->filterableAttributes->getList() as $attribute) {
                 $this->filters[] = $this->createAttributeFilter($attribute, $layer);
              }
          }
        return $this->filters;
       }
     }
    ?>

    Magento 2: Exclude group products in category page using plugin

    Magento 2: Exclude group products in list page using a plugin

    If you want to exclude group products in category page then follow below steps:

    1. Put below code in your module di.xml file.

    <type name="MagentoCatalogModelLayer">
     <plugin name="LayerPlugin" type="VendernameModulenamePluginLayer"/>
    </type>

    2. The next step is to create Layer.php file in your module and paste below code in VendernameModulenamePlugin folder

    <?php
    namespace VendernameModulenamePlugin;
    
    use MagentoCatalogApiCategoryRepositoryInterface;
    use MagentoFrameworkExceptionNoSuchEntityException;
    use MagentoCatalogModelResourceModelProductAttributeCollectionFactory as AttributeCollectionFactory;
    
    class Layer
    {
     /**
     * Product collections array
     *
     * @var array
     */
     protected $_productCollections = [];
    
     /**
     * Key which can be used for load/save aggregation data
     *
     * @var string
     */
     protected $_stateKey = null;
    
     /**
     * Core registry
     *
     * @var MagentoFrameworkRegistry
     */
     protected $registry = null;
    
     /**
     * Store manager
     *
     * @var MagentoStoreModelStoreManagerInterface
     */
     protected $_storeManager;
    
     /**
     * Catalog product
     *
     * @var MagentoCatalogModelResourceModelProduct
     */
     protected $_catalogProduct;
    
     /**
     * Attribute collection factory
     *
     * @var AttributeCollectionFactory
     */
     protected $_attributeCollectionFactory;
    
     /**
     * Layer state factory
     *
     * @var MagentoCatalogModelLayerStateFactory
     */
     protected $_layerStateFactory;
    
     /**
     * @var MagentoCatalogModelLayerItemCollectionProviderInterface
     */
     protected $collectionProvider;
    
     /**
     * @var MagentoCatalogModelLayerCategoryStateKey
     */
     protected $stateKeyGenerator;
    
     /**
     * @var MagentoCatalogModelLayerCategoryCollectionFilter
     */
     protected $collectionFilter;
    
     /**
     * @var CategoryRepositoryInterface
     */
     protected $categoryRepository;
    
     /**
     * @param LayerContextInterface $context
     * @param LayerStateFactory $layerStateFactory
     * @param AttributeCollectionFactory $attributeCollectionFactory
     * @param MagentoCatalogModelResourceModelProduct $catalogProduct
     * @param MagentoStoreModelStoreManagerInterface $storeManager
     * @param MagentoFrameworkRegistry $registry
     * @param CategoryRepositoryInterface $categoryRepository
     * @param array $data
     */
     public function __construct(
     MagentoCatalogModelLayerStateFactory $layerStateFactory,
     AttributeCollectionFactory $attributeCollectionFactory,
     MagentoCatalogModelResourceModelProduct $catalogProduct,
     MagentoStoreModelStoreManagerInterface $storeManager,
     MagentoFrameworkRegistry $registry,
     CategoryRepositoryInterface $categoryRepository,
     array $data = []
     ) {
     $this->_layerStateFactory = $layerStateFactory;
     $this->_attributeCollectionFactory = $attributeCollectionFactory;
     $this->_catalogProduct = $catalogProduct;
     $this->_storeManager = $storeManager;
     $this->registry = $registry;
     $this->categoryRepository = $categoryRepository; 
    
     }
    
     public function afterGetProductCollection(MagentoCatalogModelLayer $subject, $collection)
     {
     $collection->addAttributeToFilter('type_id', array('eq' => 'grouped'));
     return $collection;
     }
     
    }

    Magento 2 Sorting Attribute Option value in category filter programmatically

    Step 1) Override the Magento_LayeredNavigation/templates/layer/filter.phtml to your theme design directory

    Step 2) Add and Change in filter.phtml

    <?php 
        $offsets = array();
        $offsets1 = array();
        $offsets2 = array();
        $offsets3 = array();
        foreach ($filterItems as $filters) {
            $checklable = $filters->getLabel();
            if(preg_match("/[a-z]/i", $checklable)){
                array_push($offsets1, $filters);
            }
            elseif(preg_match("/,/i", $checklable)){
                array_push($offsets2, $filters);
            }
            elseif(preg_match("/-/i", $checklable)){
                array_push($offsets2, $filters);
            }
            elseif(strstr($checklable, '/')){
                array_push($offsets2, $filters);
            }
            else{
                array_push($offsets3, $filters);
            }
        }
        asort($offsets1);
        asort($offsets2);
        sort($offsets3);
    
        $offsets = array_merge($offsets3,$offsets2,$offsets1);
    ?>
    <ol class="items">
        <?php foreach ($offsets as $filterItem): ?>
            <li class="item">
                <?php //echo '===>'; ?>
                <?php if ($filterItem->getCount() > 0): ?>
                    <a href="<?= $block->escapeUrl($filterItem->getUrl()) ?>">
                        <?= /* @escapeNotVerified */ $filterItem->getLabel() ?>
                        <?php if ($this->helper('MagentoCatalogHelperData')->shouldDisplayProductCountOnLayer()): ?>
                            <span class="count"><?= /* @escapeNotVerified */ $filterItem->getCount() ?><span class="filter-count-label">
                                <?php if ($filterItem->getCount() == 1):?> <?= /* @escapeNotVerified */ __('item') ?><?php else:?> <?= /* @escapeNotVerified */ __('items') ?><?php endif;?></span></span>
                        <?php endif; ?>
                    </a>
                <?php else:?>
                    <?= /* @escapeNotVerified */ $filterItem->getLabel() ?>
                    <?php if ($this->helper('MagentoCatalogHelperData')->shouldDisplayProductCountOnLayer()): ?>
                        <span class="count"><?= /* @escapeNotVerified */ $filterItem->getCount() ?><span class="filter-count-label">
                            <?php if ($filterItem->getCount() == 1):?><?= /* @escapeNotVerified */ __('item') ?><?php else:?><?= /* @escapeNotVerified */ __('items') ?><?php endif;?></span></span>
                    <?php endif; ?>
                <?php endif; ?>
            </li>
        <?php endforeach ?>
    </ol>
    

     

    Are You Ready For 3D Secure 2.0?

    Recently there has been a significant impact on most payment processing (credit cards or bank transfers). The European Union revised a regulation known as the Payment Service Directive (PSD) with an updated version PSD2.

    Yet, many merchants do not understand what PSD2 is all about so we thought to give complete information on how it affects Magento 2 merchants and payment gateways.

    Meaning of 3D Secure:

    3D Secure is a three-domain model which helps in reducing fraud. It provides additional security for online credit and debit card transactions by adding one more layer for customer purchases.

    • Acquirer Domain — Merchant or acquirer in which credit/debit cards details are entered
    • Issuer Domain — Bank that issued credit/debit card
    • Interoperability domain — Infrastructure that supports the 3D Secure protocol, payment transaction. In most cases, the payment gateway represents the interoperability domain.

    Various financial services give their implementation of 3D Secure” Verified by Visa” from Visa, “Mastercard SecureCode” from Mastercard, “American Express SafeKey” from American Express, and “J/Secure” from JCB.

    How Does It Work?

    It uses XML messages which are sent over an SSL connection with cardholder authentication information. When you implement 3D secure in your online store, it shows a popup with a link redirecting to a bank’s page or an iframe that is offered by the issuer bank. The customers have to enter the password, SMS code, or one-time token in the popup.

    The benefit of using 3D Secure 2.0 are:

    • Frictionless Checkout Flow
    • Non-Payment authentication
    • Native Mobile Integration (support of in-app, mobile, digital wallet)
    • Better performance for end-to-end message processing
    • Prevention of unauthenticated payments, even if a cardholder’s card number is stolen or cloned

    Contextual data

    The key feature of 3DS 2.0 is analyzing the merchant’s contextual data and prompting customers to verify their identity for high-risk transactions which only constitute less than 5 percent of all payment transactions it may consist of first and last names, emails, billing addresses, and other related data and it can be shared across payment providers to enhance the analysis mechanism and transaction risks. Face and voice recognition are also part of current authentication mechanisms.

    3D Secure and PSD2

    Strong Customer Authentication (SCA) was introduced as a part of the PSD2 directive which is the new authentication requirement. It is created to reduce fraud and better security. To authenticate the payer, you will need at least two of the following factors.

    • Password or Pin
    • Phone or hardware token for authentication
    • Fingerprint & face recognition

    European banks have started declining payments that require SCA. Low risk and low-value transactions may still be accepted as well as subsequent payments in a recurring subscription. There are some uncertainties in terms of this regulation especially the application to non-EU customers or EU customers buying outside the US. Therefore, we suggest all merchants update their payment integrations to support SCA despite their location.

    What does this all mean for Magento merchants?

    According to PSD2, the EU (including the UK) will have to implement SCA (Strong Customer Authentication). SCA has already come into effect for all European eCommerce transactions which fall under PSD2. Further, in October 2019, this 3DS 2.0 scheme will be mandatory for European online business and later in 2020 3DS 2.0 will be launched worldwide.

    Magento Payment Provider Recommendations

    We have mentioned a few recommendations for the Magento native payment integration which will ensure that customers payment does got get declined

    Payment Provider Magento Commerce 2.X
    PayPal Continue using the current Magento built-in integration, as the 3D Secure 2.0 payment flow changes are all handled by PayPal.
    Authorize.net Use the official extension (recommended) or the Magento integration in upcoming version 2.3.3+ or 2.2.10+ with a 3D Secure.

    Authorize.net provides the ability, via the cardholder Authentication request field, to make 3D Secure verification via 3rd party services. Starting from Magento 2.3.3 release, Authorize.net AcceptJs integration will support 3DS 2.0.

    CyberSource  Cybersource introduced Payer Authentication API with 3D Secure 2.0 support for Secure Acceptance Hosted Checkout and Simple Order API.
    eWay Use the official extension.

    Get in touch with your payment provider to know what their recommendations are for supporting the PSD2 SCA requirements.

    Magento’s future versions will deprecate and ease the core integration in support of official payment integrations in the marketplace such as CyberSource, Authorize.net, eWay, Worldpay. To provide the latest features with free official payment extensions, the official integrators will work closely with all the vendors around the world.