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>