MISCONF Redis is configured to save RDB snapshots

Have you ever faced following problem on the server because of the redis?

MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error

We recently faced this error on our client’s live site server. Here is the solution for the same:

You will need to login into your server using SSH and hit following commands.

$ redis-cli
> config set stop-writes-on-bgsave-error no

I hope these command solve your problem and let us know if you have other solution which worked for you.

Happy coding guys!

How to change price dynamically on product page in Magento2

In this blog post, we are going to code how to dynamically change the price on product page for magento 2.

To add JS code on the product page, we have created layout file and added js file in content reference container.

<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <referenceContainer name="content">
        <block class="Magento\Framework\View\Element\Template"
               name="pricechange" as="pricechange" template="Codedecorator_Learn::product/js.phtml"/>
    </referenceContainer>
</page>

After creating layout file, lets create template file which will have our js code

require(["jquery","Magento_Catalog/js/price-utils"], function(jQuery,priceUtils) {
    var productPrice = parseFloat(jQuery('span.price-final_price > span[data-price-type="finalPrice"]').attr('data-price-amount'));
    var finalPrice=100;
    formatedPrice = priceUtils.formatPrice(parseInt(finalPrice));
    jQuery('span.price-final_price > span.price-wrapper > span.price').text(formatedPrice);
    var finalPrice=productPrice+(pricePerText*textObjects)+(pricePerImage*imageObjects);
    formatedPrice = priceUtils.formatPrice(parseInt(finalPrice));
    jQuery('span.price-final_price > span.price-wrapper > span.price').text(formatedPrice);
}); // Required

After creating both the files, just flush the cache using following command.

php bin/magento cache:flush

I hope this post will help and give the solution for any of your problem.

happy coding πŸ˜€

How to rewrite widget function with mixins Magento 2

In this post, we will understand how to use Mixins in Magento 2.

What is Mixin?

Mixins are a language concept that allows a programmer to inject some code into a class

A mixin is a class containing methods that can be used by other classes without a need to inherit from it.

Wikipedia

If we want to perform an extra action or extend a Javascript functionality without completely overwriting it, in this case mixin comes in picture. Let’s start with an example

var config = {
    config: {
        mixins: {
            'Magento_Catalog/js/catalog-add-to-cart': {
                'Codedecorator_Learn/js/cd-catalog-add-to-cart-mixin': true
            }
        }
    }
};

After creating above file, we need to create cd-catalog-add-to-cart-mixin.js file where we will extend the functionality of the submitForm function.

define([
], function () {
    'use strict';
    return function (widget) {
        $.widget('mage.catalogAddToCart', widget, {
            /**
             * Handler for the form 'submit' event
             *
             * @param {jQuery} form
             */
            submitForm: function (form) {
                this.ajaxSubmit(form); // original code
                this.doSomethingAfterSubmit(); // add every thing you want
            },
            /**
             * Do something
             */
            doSomethingAfterSubmit: function () {
                // Do stuff
            },
        });
        return $.mage.catalogAddToCart;
    }
});

Try to add product into cart and see your change is coming in or not. If not then try to flush cache or deploy the content.

Happy coding guys πŸ˜€

How to Enable Error Log in Magento2.4

Errors are the part of developer coding journey and they also improve us as developer.

A good developer always read the error carefully and find the solution for the same so let’s see how we can see error log in Magento.

Where you can find the error log

The log files are located in the <magento-root>/var/log/Β folder.

By default, Magento2 writes to the debug log (<magento-root>/var/log/debug.log) when it is in default or develop mode, but not when it is in production mode.

Enable error log

Use the bin/magento setup:config:set --enable-debug-logging=true && bin/magento c:f command to change the default value.

Disable error log

Use the bin/magento setup:config:set --enable-debug-logging=false && bin/magento c:f command to change the default value

Before Magento 2.3.1 enable error log

Use the bin/magento config:set dev/debug/debug_logging 1 && bin/magento c:f command to change the default value

Happy debugging πŸ˜€

How To Additional Options In Cart Item – Magento2

If you want to show any additional information in cart item then following code will help you out.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="checkout_cart_product_add_after">
        <observer name="set_additional_options"
        instance="Codedecorator\Learn\Observer\SetAdditionalOptions"/>
    </event>
</config>
namespace Codedecorator\Learn\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Serialize\SerializerInterface;

class SetAdditionalOptions implements ObserverInterface
{
    protected $request;
    private $serializer;

    public function __construct(
        RequestInterface $request,
        SerializerInterface $serializer
    )
    {
        $this->request = $request;
        $this->serializer = $serializer;
    }

    public function execute(EventObserver $observer)
    {
        $item = $observer->getQuoteItem();
        $post = $this->request->getPost();
		
        $additionalOptions = array();
        if ($additionalOption = $item->getOptionByCode('additional_options')) {
            $additionalOptions = $this->serializer->unserialize($additionalOption->getValue());
        }
        // check your custom condition
        if(isset($post['height'])){
			$price = 100;
            $additionalOptions[] = [
                'label' => 'Height',
                'value' => $post['height']
            ];
        }
         // Without any condition add option
         $additionalOptions[] = [
                'label' => 'CD',
                'value' => 'CD OPTION'
            ];

        if (count($additionalOptions) > 0) {
            $item->addOption(array(
                'product_id' => $item->getProductId(),
                'code' => 'additional_options',
                'value' => $this->serializer->serialize($additionalOptions)
            ));
        }
    }
}

Options will be shown on frontend side but wont show in admin order.

To set Additional in Order as well then go with following steps.

Append following code in events.xml that we have created

 <event name="sales_model_service_quote_submit_before">
        <observer name="order_add" instance="Codedecorator\Learn\Observer\SetAdditionalOptionsToOrder" />
 </event>
<?php

namespace Codedecorator\Learn\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Serialize\SerializerInterface;

class SetAdditionalOptionsToOrder implements ObserverInterface
{
    private $serializer;

    public function __construct(
        SerializerInterface $serializer
    )
    {
        $this->serializer = $serializer;
    }

    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        try{

            $quote = $observer->getQuote();
            $order = $observer->getOrder();

            foreach ($quote->getAllVisibleItems() as $quoteItem) {
              $quoteItems[$quoteItem->getId()] = $quoteItem;
            }

            foreach ($order->getAllVisibleItems() as $orderItem) {

                $quoteItemId = $orderItem->getQuoteItemId();
                $quoteItem = $quoteItems[$quoteItemId];
                $additionalOptions = $quoteItem->getOptionByCode('additional_options');

                if(!is_null($additionalOptions)) {
                    $options = $orderItem->getProductOptions();
                    $options['additional_options'] = $this->serializer->unserialize($additionalOptions->getValue());
                    $orderItem->setProductOptions($options);
                }
            }
            return $this;

        }catch (\Exception $e) {
            
             // catch error if any
        
        }
      
    }
}

Happy coding πŸ™‚

System configuration in admin Magento2

System configuration mainly used as module configuration where the admin will set some configuration for specific modules like enable/disable the module. Get some input or selection of options like that we have a variety of field to be created in the configuration.

Where you will find it?
You will be able to find the module configuration in Magento 2 admin -> Stores(Menu) -> Configuration

All the system configuration will have three parts:

  1. Tab (It is left side the system configuration page. Which contains multiple sections of multiple modules)
  2. The section will contain the groups of configuration for the specific module
  3. Each group will contain fields for the configuration

Few basic attributes are used in all the elements of system configuration are:

  1. Id: unique ID of the element
  2. Translate: To tell Magento to translate the label of the element according to the language of the admin
  3. sortOrder: Position of the element.
  4. showInDefault: Do you want to show this element when an admin hasn’t selected any scope. (Global)
  5. showInWebsite: Do you want to show this element when admin select website (Website)
  6. showInStore: Do you want to show this element when admin select store view (Store View)

Above mentioned 3,4,5 point mainly useful when we want to allow admin customer to configure the module based on the scope.

SCOPE in configuration.

Store: It is configuration limited to the store view, which is often used to display
a store in multiple languages.
Website: Configuration is limited to the website.
Global: It applies to the entire installation.

For example enable/disable of module if we can showInDefault=1 , ShowIndWebsite=1 and ShowInStore=1 then will show this field in all scope. If make Show InDefault=0, ShowInWebsite and ShowInStore=1 then it will show field in default or global scope admin have to change the scope.

<system> 
     <tab id="codedecorator" translate="label" sortOrder="10">
       <label>Codedecorator</label> <!--Tab Name-->
     </tab>
     <section id="one" translate="label" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="1">
       <class>one-section</class> <!--  Class name to section -->
       <label>Hello World</label> <!--Label Of this section -->
       <tab>codedecorator</tab> <!-- It will tell which tab is linked with this section so it should match with tab id and also can be used to group multiple section -->
       <resource>Codedecorator_Learn::codedecorator_config</resource> <!--  It used in ACL -->
       <group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>License Configuration</label>
                <!-- Field element has type attribute which decide the type of field. -->
                <field id="serial_key" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Serial Key</label>
                    <backend_model>Codedecorator\Learn\Model\Config\Backend\Lookup</backend_model>
                    <validate>required-entry</validate>
                    <comment>Take serial key from Codedecorator</comment> <!--Give a comment to field -->
                </field>
                <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Module Enable</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
       </group>
    </section>
</system>

Source Model: Source is model to provide data to field like select and multiple select.
Backend Model: Backend model is mainly useful when want to take some action before saving the configuration. Like to check serial key correct or not before saving the configuration if wrong provides a message that is not correct.

Happy coding guys!

How to delete image or file in Magento2

When we are working on image or file upload functionality from frontend of Magento, we also allowing customer to upload another or update the image so that time we also have to delete previously upload the image or file as we can left those uploaded image or file left on server.

Following code will help you with that logic.

<?php
namespace Codedecorator\Learn\Controller\Index;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Filesystem\Driver\File;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;

class Delete extends Action
{

    protected $_filesystem;
    protected $_file;

    public function __construct(
        Context $context,
        Filesystem $_filesystem,
        File $file
    )
    {
        parent::__construct($context);
        $this->_filesystem = $_filesystem;
        $this->_file = $file;
    }

    public function execute()
    {
        ........
        $fileName = "file or image name";// replace this with some codes to get the $fileName
        $mediaRootDir = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA)->getAbsolutePath();
        if ($this->_file->isExists($mediaRootDir . $fileName)) {
            $this->_file->deleteFile($mediaRootDir . $fileName);
        }
       ......
    }
}

Let’s us know if you need any issue or any query.

Happy coding guys!

How to create a new table using Declarative Schema( db_schema.xml) in Magento2

We all know about PHP script like InstallSchema.php (old way to create new table) to create new table but magento has introduced Declarative schema to create or maintain the tables using declarative schema xml.

Advantage of this db_schema.xml is to avoid the unnecessary version checking for create new columns or delete column for each table change. Let’s create one table

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
   <table name="codedecorator_learn" resource="default" engine="innodb" comment="Codedecorator Learn">
      <column xsi:type="int" name="id" padding="10" unsigned="false" nullable="false" identity="true" comment="ID" />
      <column xsi:type="varchar" name="name" nullable="false" length="25" comment="Name" />
      <column xsi:type="varchar" name="email" nullable="false" length="25" comment="Email" />
      <column xsi:type="varchar" name="description" nullable="false" length="255" comment="Description" />
      <column name="created_at" nullable="false" xsi:type="datetime" default="CURRENT_TIMESTAMP" on_update="false"/>
      <column name="updated_at" nullable="true" xsi:type="datetime" default="CURRENT_TIMESTAMP" on_update="true"/>
      <constraint xsi:type="primary" referenceId="PRIMARY">
         <column name="id" />
      </constraint>
   </table>
</schema>

After creating db_schema.xml, we need to run following command to tell magento that we have created new table and also for backward compatibility with php script.

It will not directly delete any column or table if you dont have entry in whitelist because there might be upgrade schema available for the same so that it is important to generate whitelist.

php bin/magento setup:db-declaration:generate-whitelist --module-name=Codedecorator_Learn

Note: When you disable any module with db schema and you run setup upgrade command then it will delete the module table directly if you want to avoid that situation.

Please ran command using setup:upgrade --safe-mode=1 in order to create a database backup and then eventually setup:upgrade --data-restore=1 if you enable the module back and wish to restore from that backup.

Happy coding guys!

File Upload in System Configuration in Magento2

Following files and steps will help you to add file upload in system configuration with custom file type validation.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="codedecorator" class="code-config-tab"  translate="label" sortOrder="300"> 
            <label><![CDATA[Decorator Extensions]]></label> 
        </tab> 
        <section id="codedecorator_learn" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Company Agreement</label>
            <tab>codedecorator</tab>
            <resource>Codedecorator_Learn::config</resource>
            <group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
                <label>General</label>
               <field id="custom_file_upload" translate="label" type="Magento\Config\Block\System\Config\Form\Field\File" sortOrder="6" showInDefault="1" showInWebsite="1" >
                <label>Upload custom file</label>
                <backend_model>Codedecorator\Learn\Model\Config\Backend\File</backend_model>
                <upload_dir config="system" scope_info="1">cdlearn</upload_dir>
            </field>
            </group>
        </section>
    </system>
</config>
<?php

namespace Codedecorator\LearnCompanyAccount\Model\Config\Backend;

class File extends \Magento\Config\Model\Config\Backend\File
{
    public function _getAllowedExtensions() {
        return ['pdf'];
       //return ['pdf','svg','jpeg'];
    }
}

We have requirement of PDF file only so we have pub/media/cdlearn/defaultkept pdf only if you have other extension to be checked or validate than just remove the comment. Uploaded file will be available in the pub/media/cdlearn/default folder

Magento 2 : Deprecated Functionality: Function ReflectionType::__toString() is deprecated

If you are stuck with above problem, we have solution for you which mentioned below.

File: vendor/zendframework/zend-code/src/Reflection/ParameterReflection.php
OR
File: vendor/laminas/laminas-code/src/Reflection/ParameterReflection.php

In function detectType() replace line return (string) $type; with return $type->getName();
File: vendor/magento/framework/DB/Sql/UnionExpression.php

In function __toString() replace line $sql = implode($parts, $this->type); with $sql = implode($this->type,$parts);
File: vendor/magento/framework/App/AreaList.php

In function getCodeByFrontName() replace line if ($areaInfo['frontName'] == $frontName) with if (isset($areaInfo) && $areaInfo['frontName'] == $frontName)

I hope this solution help you out and let’s know if you have any doubt or any other solution which worked for you.

Happy Coding!