Magento 2 Solid Principle

As soon as you start working with OOPs we came to know about sevral design pattern and SOLID principle is the first thing that we can across. As per magento technical guidelines (under class design)

Object decomposition MUST follow the SOLID principles

If you check the Magento code than you can clearly see that Magento code is following SOLID principles. We will look into it these principle one by one:-

SOLID stands for:

  • Single Responsibility
  • Open-Closed
  • Liskov Substitution
  • Interface Segregation
  • Dependency Inversion

Single Responsibility Principle

A class should have only and only one reason to change.

As its name suggest, it states that every object should have single responsibility i.e, single role to perform and that responsibility should be entirely encapsulated by the class. It means that when we are adding some unrelated feature they should not affect same class.
For example, In Magento 2 every controller has just one responsibility.

namespace Magento\Cms\Controller\Page;

class View extends \Magento\Framework\App\Action\Action {

    public function execute() {

        $pageId = $this->getRequest()->getParam('page_id', $this->getRequest()->getParam('id', false));
        $resultPage = $this->_objectManager->get(\Magento\Cms\Helper\Page::class)->prepareResultPage($this, $pageId);
        if (!$resultPage) {
            $resultForward = $this->resultForwardFactory->create();
            return $resultForward->forward('noroute');
        }
    return $resultPage;
    }
}

Open-Closed Principle

Objects or entities should be open for extension, but closed for modification.

The above statement itself explains everything that it should be always easy to make behaviour change of particular implementation but we should make changes directly into the original source code. We should make changes by extending the class.

For example, In Magento 2 if you check module-catalog/Controller/Adminhtml/Category.php it is just initiating category object and rest of the actions (such as:- move, refresh page) are using it by extending the \Magento\Catalog\Controller\Adminhtml\Category class and making behaviour changes as per the requirement.

namespace Magento\Catalog\Controller\Adminhtml\Category;

class Move extends \Magento\Catalog\Controller\Adminhtml\Category {

    public function execute() {
        
        $parentNodeId = $this->getRequest()->getPost('pid', false);
        $prevNodeId = $this->getRequest()->getPost('aid', false);
        ............
    }
}

Liskov substitution principle

This principle states that any substitute/child class (implemented by either extension or dependency injection) should adhere the same rules as its predecessor class i.e, it should not break the functionality that is already being used by keeping method signature consistent.

For example, in below example you can see that toOptionArray() in both class return an array


namespace Magento\Ups\Model\Config\Source;

use Magento\Shipping\Model\Carrier\Source\GenericInterface;

class Generic implements GenericInterface {

    public function toOptionArray()
    {
        $configData = $this->carrierConfig->getCode($this->_code);
        $arr = [];
        foreach ($configData as $code => $title) {
            $arr[] = ['value' => $code, 'label' => __($title)];
        }
        return $arr;
    }
}
namespace Magento\Ups\Model\Config\Source;

class OriginShipment extends \Magento\Ups\Model\Config\Source\Generic

{
    protected $_code = 'originShipment';
    public function toOptionArray()
    {
        $orShipArr = $this->carrierConfig->getCode($this->_code);
        $returnArr = [];
        foreach ($orShipArr as $key => $val) {
            $returnArr[] = ['value' => $key, 'label' => $key];
        }
        return $returnArr;
    }
    return $returnArr;
    }
}

Interface Segregation

This states that we should not use those method or properties in the interface that the client may not use. Rather than having all method which are related to responsibility it’s good to break it into multiple interfaces and let a single class extends multiple interface. For example see the below image:-

Dependency Inversion principle

High level module should not depend on low level moduleFirst of all keep in mind that dependency inversion is not same as dependency injection, although they go hand in hand.

Magento 2 Design Pattern

There are lots of design pattern used in Magento, here we will be discussing some of them. We all know design patterns are the solution of some commonly occurring problem during our development phase. They are the recommended way to write our code.

Service Contract Design Pattern

Magento is an extension based or modular system, which allows a third-party developer to customize and overwrite core parts of its framework. These customizations may lead to several issues, for example, it will become for developers to keep track of customization done by external extensions. Thus to overcome this Magento comes up with service contract pattern. A service contract is a set of interfaces which act as a layer between an end user and business layer. Thus rather than directly exposing business logic for customization to end user, a layer called service contract comes in between.

  • Service contracts enhances the modularity of Magento.
  • Helps merchants for easy upgrade of Magento
  • Ensure well-defined and durable API that other external and Magento module implements.
  • Provide an easy way to expose business logic via REST or SOAP interfaces.

Object Manager

It itself consist of various pattern such as:- Dependency injection, Singleton, Factory, Abstract Factory, Composite, strategy, CQRS, Decorator and many more. We will discussing some most used pattern among these.
Object manager has a very big role to play, Magento prohibits the direct use of it. Object manager is responsible for implementing factory, singleton and proxy patterns. It automatically instantiates parameter in class constructors. Before moving future lets understand about injectable and non-injectable objects:-

Injectable objects

They does not have their own identity such as EventManager, CustomerAccountManagementService.

Non-injectable objects

Such as customer, product etc. These entities usually have their identities and state, since they have their identities it is important to know on which exact instance of entity we have to work.

Dependency Injection

It is an alternative to Mage in magento 1. It is a concept of injecting the dependent object through external environment rather than creating them internally. Thus we will be asking for resource when our object is being created instead of creating resource when needed. This helps in future modification and testing becomes very easy by mocking required objects. For example:-

 

 

 

 

 

 

 

 

So in the first image, we have read() method in class A which has initialized database connection object but in second image we have injected database connection via a constructor. Now to test our database connection we can easily create mock object during object creation of class A, making our testing much easier. For large object injection Magento have introduced object manager. But using object manager is not a good practice so we use Factory classes and proxy.

Factory pattern or Factory classes:-

In Magento 2 Factory classes create a layer between the object manager and business code. Factory classes need not define explicitly as they are auto-generated. We should create factory classes for non-injectable objects.
Let see the way to get create factory classes:-

function __construct ( \Magento\Cms\Model\BlockFactory $blockFactory) {
$this->blockFactory = $blockFactory;
}

// calling create will give the object for block class
$block = $this->blockFactory->create();

Event Observer pattern

Events are a way to hook up your functionality in between of code without overriding any core class. An event is dispatched when some actions get triggered. They are very useful when we have to just modify some value of an object rather than overriding that class we can event add an observer to do that. It also prevents someone to touch our core code, anyone can use them without deep knowledge of logic running behind. You can create your own event also by writing following line of code:-

$this->eventManager->dispatch('my_module_event_after',['myEventData'=>$eventData]);

An observer is certain type of Magento classes that contains the logic which you want to implement in any event. Below is an example of observer class:-

namespace MyCompany\MyModule\Observer;

use Magento\Framework\Event\ObserverInterface;

class MyObserver implements ObserverInterface
{
  public function __construct()
  {
    //Observer initialization code...
    //You can use dependency injection to get any class this observer may need.
  }

  public function execute(\Magento\Framework\Event\Observer $observer)
  {
    //Observer execution code...
  }
}

Proxy Pattern

Proxy classes are used to work in place of another class and in Magento 2 they are sometimes used in place of resource hungry classes. To understand what proxy classes do let’s see the reason which leads to the occurrence of proxy classes. As we know Magento uses constructor injection for object creation and when we instantiate an object all the classes in its constructor will also instantiate thus leading to a chain of instantiation via a constructor, this can really slow down the process and impact the performance of an application, so to stop chain instantiation Magento uses proxy classes.

Lets see following code:-

Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy

Magento\Catalog\Model\Product\Link\Proxy

So in above code, we are using proxy classes for catalogProductStatus and productLink. When we run

 php bin/magento setup:di:compile 

Magento creates proxy classes on the fly using di.xml with some fixed conventions, thus replacing the original object with a proxy class object. Now let us look at our proxy class to understand how it is working

namespace Magento\Catalog\Model\Product\Attribute\Source\Status;
 
class Proxy extends \Magento\Catalog\Model\Product\Attribute\Source\Status implements \Magento\Framework\ObjectManager\NoninterceptableInterface
 
{
 
protected $_objectManager = null;
protected $_instanceName = null;
protected $_subject = null;
 
protected $_isShared = null;
 
/**
 
* Proxy constructor
 
*
 
* @param \Magento\Framework\ObjectManagerInterface $objectManager
 
* @param string $instanceName
 
* @param bool $shared
 
*/
 
public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = '\\Magento\\Catalog\\Model\\Product\\Attribute\\Source\\Status', $shared = true)
 
{
 
$this->_objectManager = $objectManager;
 
$this->_instanceName = $instanceName;
 
$this->_isShared = $shared;
 
}
 
/**
 
* @return array
 
*/
 
public function __sleep()
 
{
 
return ['_subject', '_isShared', '_instanceName'];
 
}
 
/**
 
* Retrieve ObjectManager from global scope
 
*/
 
public function __wakeup()
 
{
 
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
 
}
 
public function __clone()
 
$this->_subject = clone $this->_getSubject();

}
 
…..........

Some common convention Magento follow while creation of proxy:-

  • Namespace of proxy class will be same as original (Magento\Catalog\Model\Product\Attribute\Source\Status)
  • Proxy class only extends one object i.e, object manager
  • Has magic functions such as __sleep, __wake which are invoked only on certain action and function such as __clone will make an object of original class and will provide the object only when it is needed (making use of lazy loading design pattern), thus improving the performance of application

Magento2.2 add custom product tab with custom template on product edit page

In this blogpost we will learn how to add custom tab in product edit page in admin panel.Create a file with mentioned path and add the following code:-

view\adminhtml\ui_component\product_form.xml
 <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <htmlContent name="qrcode" sortOrder="22">
        <argument name="data" xsi:type="array">
            <item name="wrapper" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">Qrcode</item>
                <item name="collapsible" xsi:type="boolean">true</item>
                <item name="opened" xsi:type="boolean">false</item>
            </item>
        </argument>
        <settings>
            <wrapper>
                <canShow>true</canShow>
                <componentType>fieldset</componentType>
            </wrapper>
        </settings>
        <block class="W3solver\Qrcode\Block\Adminhtml\Catalog\Product\Edit\Tab\Qrcode" name="qrcode">
            <arguments>
                <argument name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Qrcode</item>
                    <item name="collapsible" xsi:type="boolean">true</item>
                    <item name="opened" xsi:type="boolean">false</item>
                    <item name="sortOrder" xsi:type="string">11</item>
                    <item name="canShow" xsi:type="boolean">true</item>
                    <item name="componentType" xsi:type="string">fieldset</item>
                </argument>
            </arguments>
        </block>
    </htmlContent>
</form>

Lets understand above code. product_form is used for customization of product edit page in admin panel as per magento documentation(for more info refer the link). So above code will add some htmlcontent which will thus render a block to show your custom tab and its template.

Now lets create our block at below path

 W3solver\Qrcode\Block\Adminhtml\Catalog\Product\Edit\Tab\Qrcode.php 
<?php
/**
 * W3solver.com
 * @category    W3solver
 * @package     W3solver_Qrcode
 * @Description Block to add qrcode tab to product edit page
 * @author      W3solver
 * @copyright   Copyright (c) 2017 w3solver
 */
namespace W3solver\Qrcode\Block\Adminhtml\Catalog\Product\Edit\Tab;

use Magento\Backend\Block\Template\Context;
use Magento\Framework\Registry;
use W3solver\Qrcode\Model\QrcodeListFactory;

class Qrcode extends \Magento\Framework\View\Element\Template {

    protected $_template = 'product/edit/qrcode.phtml';
    protected $_qrcodeListFactory;
    protected $fileName;

    /**
     * Core registry
     *
     * @var Registry
     */
    protected $_coreRegistry = null;

    /**
     * @param Magento\Backend\Block\Template\Context
     * @param Magento\Framework\Registry
     * @param W3solver\Qrcode\Model\QrcodeListFactory
     * @param Magento\Store\Model\StoreManagerInterface
     */
    public function __construct(
    Context $context, Registry $registry, QrcodeListFactory $qrcodeListFactory, array $data = []
    ) {
        $this->_coreRegistry = $registry;
        $this->_qrcodeListFactory = $qrcodeListFactory;
        
        parent::__construct($context, $data);
    }

    /**
     * Retrieve product
     *
     * @return \Magento\Catalog\Model\Product
     */
    public function getProduct() {
        return $this->_coreRegistry->registry('current_product');
    }
}

We also need to create our template file to show whatever content we want at following path:-

w3solver\Qrcode\view\adminhtml\templates\product\edit\qrcode.phtml

So above code will craete a Qrcode tab on product edit page in admin. Drop in your comments if you face any issue.

Magento 2 some important coding practice for developers

In my previous blogpost for code sniffer, we have come to know how we can find if our extension is following MEQP2 standards. Today let us see some most common coding practice that will help us while developing Magento extension. This will make our code more magento friendly.

Use dependency injection not object manager

We all love to use object manager though we know it is not a recommended practice by Magento. We should not use it for several reason:-

a. To maintain code consistency
b. Testing become easier(as we can pass mock object as an argument)
c. Dependency injection make dependencies more clear – as everything can be easily found in constructor rather than hidden in middle of the code.
d. It also prevent us from using repeated dependency as we can make use already existing dependency of parent class

There are several exception to this scenario:-

• in static magic methods like __wakeup, serialize, etc
• in case you should make backward compatibility of constructor
• in global scope, like in fixtures of integration test.
• in class that needs only for creation of object like factory, proxy , etc


You can go through http://www.javacreed.com/why-should-we-use-dependency-injection/ for more details about dependency injection.

Do not use session in dependency injection

Whenever you will check your code with MEQP2 standard it will raise an issue if you have used session object in constructor as dependency injection. You will get below error. You can simply replace it from parent class session objects.

“Session object MUST NOT be requested in constructor. It can only be passed as a method argument. “

Use Magento exception library not core PHP – Do not throw exception using PHP keyword.

You can use message manager to show error, warning or ant exception message rather than using throw keyword.
Include \Magento\Framework\Message\ManagerInterface $messageManager in your constructor and call the respective function to show error, warning or exception.

Use magento library function instead of core PHP function

For example for curl request instead of using curl_init, curl_setopt, curl_exec use the function provide by curl class in magento (\Magento\Framework\HTTP\Client\Curl $curl)

Magento 2 – Address verification using USPS API on checkout.

This blogpost is related with address verification using USPS on one page checkout page. Address verification is a very critical part of any E-Commerce application. We have to make sure that address is deliverable, in this post we are using USPS APIs for this.

Note: In this post we are only validating US address as USPS stands for United State Postal Services. 

You can create a different module for this, or if you already have then add below code. If you don’t know how to create module in Magento 2 Then, Follow this.

Let’s verify the address in onepage checkout before order place.

Here I am using my existing module and making some changes for the same.

I have a module /var/www/html/Project_name/app/code/W3solver/TwilioIntegration/. I am using Ubuntu so my path is like this if anyone is using Mac or window path till project name will vary. Create a di.xml file on below path and add the given code, which will override magento core checkout module payment information model:

/var/www/html/Project_name/app/code/W3solver/TwilioIntegration/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Checkout\Model\PaymentInformationManagement" type="W3solver\TwilioIntegration\Model\PaymentInformationManagement" />
</config>

Now create the overriden file (PaymentInformationManagement.php) at below path.

/var/www/html/bigmoon/app/code/WestAgile/TwilioIntegration/Model/PaymentInformationManagement.php

Add the below code to your file

<?php
namespace  W3solver\TwilioIntegration\Model;

use Magento\Framework\Exception\CouldNotSaveException;
class PaymentInformationManagement extends \Magento\Checkout\Model\PaymentInformationManagement {

    public function savePaymentInformationAndPlaceOrder(
        $cartId,
        \Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
        \Magento\Quote\Api\Data\AddressInterface $billingAddress = null
    ) {

    	$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

        if(Your_condition )// if you want run this in every condition if(1)
        {
           $address_details = $billingAddress->getStreet();
           $address_detail = $address_details[0];
           $final_address = $address_detail.",".$address_details[1];
           $city = $billingAddress->getCity();
           $state = ($billingAddress->getRegionId() =='12')? 'CA' : 'navada';
           $zipcode = $billingAddress->getPostCode();

           $response =  $this->getStateName($final_address,$city,$state,$zipcode);

           if($response==0)
           {
              throw new CouldNotSaveException(__('Please re-check your shipping address'));
              return false;

           }else{
           $this->savePaymentInformation($cartId, $paymentMethod, $billingAddress);
           try {
                $orderId = $this->cartManagement->placeOrder($cartId);
           } catch (\Exception $e) {
                throw new CouldNotSaveException(
                __('An error occurred on the server. Please try to place the order again.'), $e
                );
           }
            return $orderId;
        }
            //echo $response;
        }
    }

    public function getStateName($final_address,$city,$state,$zipcode){
        $input_xml = <<<EOXML
               <AddressValidateRequest USERID="Your USPS User ID will Come here">
               <Revision>1</Revision>
               <Address ID="0">
               <Address1>$final_address</Address1>
               <Address2></Address2>
               <City>$city</City>
               <State>$state</State>
               <Zip5>$zipcode</Zip5>
               <Zip4></Zip4>
               </Address>
               </AddressValidateRequest>
        EOXML;

        $fields = array(
                'API' => 'Verify',
                'XML' => $input_xml
            );
        $url = 'http://production.shippingapis.com/ShippingAPITest.dll?' . http_build_query($fields);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
        $data = curl_exec($ch);
        curl_close($ch);

        // Convert the XML result into array
        $array_data = json_decode(json_encode(simplexml_load_string($data)), true);

        if (array_key_exists("Error",$array_data['Address'])){
            return  0;
        }else{
            return 1;
        }
  }
}

Go to your Magento 2 installation from terminal and run “php bin/magento setup:upgrade“.

Try this solution and let us know your thoughts.

Magento 2 place order without a Email address

This blogpost guide you a hack to place order in magento 2 via Email. As we all know email is an essential component for managing data in Magento. But today i came across to one of my client who does not want email to be necessary, i told him that this is not possible in Magento but he wanted to implement such functionality. So i came across a hack, i removed validation from client side and passed a random generated email. Here are the steps that i followed:-

To achieve this we need to override Magento_Checkout/js/action/place-order file and Magento_Checkout/template/form/element/email.html, to do that create a requirejs-config file in your module under view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            'Magento_Checkout/template/form/element/email.html': 
            'W3solver_Trackorder/template/form/element/email.phtml',
	    'Magento_Checkout/js/action/place-order': 
            'W3solver_Trackorder/js/action/place-order'
        }
  }
};

Place your overriden file under view/frontend/web/js/action/place-order.js and add the below code. Here we have just added an optional email(emailOptional@w3solver.com), so all order will be placed by this email.

/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
define(
    [
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/url-builder',
        'Magento_Customer/js/model/customer',
        'Magento_Checkout/js/model/place-order'
    ],
    function (quote, urlBuilder, customer, placeOrderService) {
        'use strict';

        return function (paymentData, messageContainer) {
            var serviceUrl, payload, email;

            payload = {
                cartId: quote.getQuoteId(),
                billingAddress: quote.billingAddress(),
                paymentMethod: paymentData
            };

            if (customer.isLoggedIn()) {
                serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {});
            } else {
                serviceUrl = urlBuilder.createUrl('/guest-carts/:quoteId/payment-information', {
                    quoteId: quote.getQuoteId()
                });
                
                /* added email optional*/
                if(quote.guestEmail == null) {
                    email = 'emailOptional@w3solver.com';
                }
                payload.email = email;
            }

            return placeOrderService(serviceUrl, payload, messageContainer);
        };
    }
);

Now we will look how to make email as non-required field, for that we have overridden core email.html file, place your overriden file under view/frontend/web/template/form/element/email.phtml and remove the required from email input as seen in below code:-

<div class="field">
            <label class="label" for="customer-email">
                <span data-bind="i18n: 'Email Address (Optional)'"></span>
            </label>
            <div class="control _with-tooltip">
                <input class="input-text"
                       type="email"
                       data-bind="
                            textInput: email,
                            hasFocus: emailFocused"
                       name="username"
                       data-validate="{'validate-email':true}"
                       id="customer-email" />
                <!-- ko template: 'ui/form/element/helper/tooltip' --><!-- /ko -->
                <span class="note" data-bind="fadeVisible: isPasswordVisible() == false"><!-- ko i18n: 'You can create an account after checkout.'--><!-- /ko --></span>
            </div>
        </div>

Magento 2 track order issue (track order link not working when an order has more than one shipment)

Magento 2 track order issue (track order link not working when an order has more than one shipment)
This blogpost has a fix for core magento issue i.e, track order not working when more than one shipment is placed. Below is the screen you will get when you will place more than one shipment for a single order.

This issue is because in popup.phtml we have called child block to show every shipment details but if you check below code

'shipping.tracking.details.' . $counter 

Above code only has counter which is index of an array of track details and for every shipment it will take counter as 0, thus we will get an issue that block already called or exist. To fix this we have to make it unique so I have added ship id to this. You need to override popup.phtml in your code and replace it with below code:-

<?php
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

use Magento\Framework\View\Element\Template;

// @codingStandardsIgnoreFile

/** @var $block \Magento\Shipping\Block\Tracking\Popup */

$results = $block->getTrackingInfo();
?>
<div class="page tracking">
    <?php if (!empty($results)): ?>
        <?php foreach ($results as $shipId => $result): ?>
            <?php if ($shipId): ?>
                <div class="order subtitle caption"><?php /* @noEscape */ echo $block->escapeHtml(__('Shipment #')) . $shipId; ?></div>
            <?php endif; ?>
            <?php if (!empty($result)): ?>
                <?php foreach ($result as $counter => $track): 
                if(!is_array($track)) {
                ?>
                    <div class="table-wrapper">
                        <?php
                            $block->addChild('shipping.tracking.details.' . $counter . $shipId, Template::class, [
                                'track' => $track,
                                'template' => 'Magento_Shipping::tracking/details.phtml',
                                'storeSupportEmail' => $block->getStoreSupportEmail()
                            ]
                        );
                        ?>
                        <?php /* @noEscape */ echo $block->getChildHtml('shipping.tracking.details.' . $counter. $shipId); ?>
                    </div>
                    <?php if (!empty($track->getProgressdetail())): ?>
                        <?php
                            $block->addChild('shipping.tracking.progress.'. $counter, Template::class, [
                                'track' => $track,
                                'template' => 'Magento_Shipping::tracking/progress.phtml'
                            ]);
                        ?>
                        <?php /* @noEscape */ echo $block->getChildHtml('shipping.tracking.progress.' . $counter); ?>
                    <?php endif; 

                }
                    ?>
                <?php endforeach; ?>
            <?php else: ?>
                <div class="message info empty">
                    <div><?php echo $block->escapeHtml(__('There is no tracking available for this shipment.')); ?></div>
                </div>
            <?php endif; ?>
        <?php endforeach; ?>
    <?php else: ?>
        <div class="message info empty">
            <div><?php echo $block->escapeHtml(__('There is no tracking available.')); ?></div>
        </div>
    <?php endif; ?>
    <div class="actions">
        <button type="button"
                title="<?php echo $block->escapeHtml(__('Close Window')); ?>"
                class="action close"
                onclick="window.close(); window.opener.focus();">
            <span><?php echo $block->escapeHtml(__('Close Window')); ?></span>
        </button>
    </div>
</div>

Configure Code Sniffer With Netbeans (MEQP2 OR Magento 2 standard)

CodeSniffer, an essential code review tool which helps you to make your code clean and consistent, specially when we work with Magento we should make it our habit to use such tools to have better development process. This blogpost is for those who uses NetBeans IDE for Magento 2 development, here are the steps to integrate PHP CodeSniffer with NetBeans IDE using MEQP2 standards.

Note:- Make sure that you have code sniffer already steup as this blogpost only explains code sniffer configuration with netbeans.

Code Sniffer configuration steps in netbeans:

• Open NetBeans IDE (this process is for Netbeans 8.2).
• Go to tools-> Plugin and install the NetBeans plugin ‘phpCS-MD’. You will get it under “Available Plugins” tab. If it does not show then check next step, it will be already setup then.

• Now go to “Tools” -> “Options”->”PHP”->”Code Analysis”.

• Click on the tab “CodeSniffer”.
Here you have to provide the path to phpcs.bat file present under your PHP installation directory. Do this by clicking the “Browse” button there.

• Now you need to select the coding standard for which you want to test your application, in our case it is MEQP2. Select it and click ok to complete configuration.

Testing Code using code sniffer in netbeans:

To test the code just select file or folder from project directory and the go to source > inspect to get the below screen:-

Click on Inspect button to get the list of all issues and warning:-

Hope this helps, just drop in your comment for any issue.

Magento 2 UI Component simplest way to show only time for date column

This is a very short blog, but i wrote this post to provide the quick and best solution to hide time for date column n UI component. For this, you have to simply provide the date format setting without time format as given below in your ui-component.xml file

<column name="salesperson_createddt" class="Magento\Ui\Component\Listing\Columns\Date" >
    <argument name="data" xsi:type="array">
      <item name="config" xsi:type="array">
         <item name="filter" xsi:type="string">dateRange</item>
         <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
         <item name="dataType" xsi:type="string">date</item>
         <item name="label" xsi:type="string" translate="true">Created Date</item>
         <item name="dateFormat" xsi:type="string">MMM d, Y</item>
      </item>
   </argument>
</column>

Magento 2 add complex (aggregate sql function) collection query for grid

This blogpost provides solution to most common problem which we face several time while creating grid. If you have some complex collection query for grid creation specially if you are using aggregate function then you might face issue with filters, export csv functionality.

Magento 2 grid Creation with custom query

For adding custom query we need to override __initSelect function in our collection file as shown in below code:-

namespace Namespace\ModuleName\Model\ResourceModel\SpCommission;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

class Collection extends AbstractCollection {

    protected function _initSelect() {
        parent::_initSelect();
        $ObjectManager = \Magento\Framework\App\ObjectManager::getInstance();

        $this->getSelect()->columns('created_dt as salesperson_createddt');
        $connection = $this->_resource->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION);
        $table = $connection->getTableName('i95dev_ar_book');
        $this->getSelect()->join(array('options' => $table), '`main_table`.`i95dev_ar_book_id` = `options`.`primary_id`');
        $this->getSelect()->columns('SUM(salesperson_commision) as total_commission')
            ->group(array('gp_salesperson_id'));
        $this->addFilterToMap('salesperson_createddt', 'main_table.created_dt');
        $this->addFilterToMap('primary_id', 'main_table.primary_id');

    }
}

So above is the way to customize your collection query for grids. Now we will see the tips for some issue you might face:-

Field is ambiguous

Suppose you joined two table and both have same name field and you are using it in filters then ‘field is ambiguous’ is most common issue you will face, to avoid it you need to map field with it alias and use alias everywhere rather than field name.

$this->addFilterToMap('primary_id', 'main_table.primary_id');

total_commission(Aggregate function field) is not a column

Note:- Magento 2 grid are made to work for simple queries with not much complexity

This happens when we do filter because as you can see above we are using ‘group by’ and aggregate function. If you see getSelectCountSql() function in \Magento\Framework\Data\Collection\AbstractDb file

public function getSelectCountSql()
    {
        $this->_renderFilters();

        $countSelect = clone $this->getSelect();
        $countSelect->reset(\Magento\Framework\DB\Select::ORDER);
        $countSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT);
        $countSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET);
        $countSelect->reset(\Magento\Framework\DB\Select::COLUMNS);

        if (!count($this->getSelect()->getPart(\Magento\Framework\DB\Select::GROUP))) {
            $countSelect->columns(new \Zend_Db_Expr('COUNT(*)'));
            return $countSelect;
        }

        $countSelect->reset(\Magento\Framework\DB\Select::GROUP);
        $group = $this->getSelect()->getPart(\Magento\Framework\DB\Select::GROUP);
        $countSelect->columns(new \Zend_Db_Expr(("COUNT(DISTINCT ".implode(", ", $group).")")));
        return $countSelect;
    }

Above you can see that our collection column, limit offset, count , order and all other things are being reset to get the count of result. Since column get reset how will magneto know what is total_commission as this is not already created column name in any table thus you need to override this method to make customization according to your own needs.

Aggregate function column Alias is not working with filter due to where clause

Here mapping filter will be a bad suggestion, thus you need to override addFieldToFilter() function and request for having clause rather than where to make your code work.