5 Steps To Setup Amazon CloudFront in Magento 2 and Clean CDN Cache

Often, when we need to speed up the website performance, it’s a good idea to relocate the static content, such as images, CSS and JavaScript files, from your website domain. We can put this content to some external content distribution network (CDN). In our case, we can rely on Amazon CDN services.

Amazon provides their CDN solution called Amazon CloudFront CDN which is the part of their AWS services. But if we need to make some changes in the content of static resources, we must also update them on the CDN side. We are glad to introduce Magento® 2 module which solves this problem.

Our task for today is to clean Amazon CloudFront Cache in order to make it empty. We should break it into two phases. The first one is settings on the Amazon side, and the second one is the most interesting – configuring Magento 2 module. Let’s start our project!

Setting Up Amazon CloudFront for Magento 2

First of all, we need to config CloudFront Distribution on Amazon side. We can configure the one CloudFront Distribution for the one domain name. That’s why in case there are several websites hosted on Magento, we should create the apart CloudFront Distribution for each domain name. Each CloudFront Distribution will have the unique identifier which we can use later for API requests.

URL which represents CloudForm Distribution should be defined on Magento side:

Stores -> Configuration -> General -> Web -> Base URLs.

We should define it in Base URL for Static View File and Base URL for User Media Files fields. We should write this URL for both cases, secure and unsecure URLs. After that, the static content will be requested by the CDN URL. If the content is not there, Amazon CloudFront will request it from the website and then cache it.

Finally, we should generate the key pair on the Amazon side. It’s a secret key for connecting to Magento using Amazon API.

Creating Magento 2 Module for Using Amazon CloudFront CDN

First, we’ll need the AWS SDK library to implement Magento 2 module for CDN. The easiest way to install the AWS SDK library on Magento 2 is to add the row to the “require” section of composer.json file. We should add the following row:

“aws/aws-sdk-php”: “3.x”

Then we should run the “composer update” command in the console. The AWS library will be successfully uploaded to vendor/aws directory.

Now we can move to Magento 2 module implementation. The module will consist of:

  • a system configuration;
  • a block which we’ll display on cache management page in admin panel;
  • a controller which will process the form submit.

So, what’s then? We’ll go through each step providing the code samples. Ready-steady-go!

Magento 2 System Configuration for Amazon CloudFront CDN

etc/admihtml/system.xml file will look like the following:

<?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>
        <section id="amazoncloudfront" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Amazon Cloudfront</label>
            <tab>mg_extensions</tab>
            <resource>Web4pro_Cloudfront::config</resource>
            <group id="general" translate="label" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General</label>
                <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1">
                    <label>Enable Amazon Cloudfront Cache Invalidate</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="access_key" translate="label comment" type="text" sortOrder="20" showInDefault="1">
                    <label>Access Key</label>
                </field>
                <field id="secret_access_key" translate="label comment" type="text" sortOrder="30" showInDefault="1">
                    <label>Secret Access Key</label>
                </field>
                <field id="amazon_region" translate="label comment" type="text" sortOrder="40" showInDefault="1">
                    <label>Amazon Region</label>
                </field>
                <field id="identifier" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Amazon Cloudfront Identifier</label>
                </field>
            </group>
        </section>
    </system>
</config>

This configuration allows the user to turn on or turn off the module in Magento 2, set and generate Access Key and Secret Key on the Amazon side, define the region.

Amazon requires specifying the region for connection because not all AWS services are available for all regions. Fortunately, Amazon CloudFront is available in all valid regions. For instance, we can specify us-east-1. All configuration parameters are global, except the identifier which we can specify for any store view. As said before, each domain should have the apart identifier on Amazon CloudForm. That’s why ids are unique.

Displaying the Block on Cache Management Page in Magento 2

Now, let’s display the block on the cache management page in the admin panel. We can do it with “layout adminhtml cache index.xml” file where we’ll define the block and its template.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="cache.additional">
            <block class="Web4pro\Cloudfront\Block\Cache" name="cache.cloudfront" template="Web4pro_Cloudfront::cache.phtml"/>
        </referenceBlock>
    </body>
</page>

We’ll define “canShow” method in the block. This method will check whether the module is turned on by the administrator or not.

public function canShow(){
        return $this->_scopeConfig->getValue('amazoncloudfront/general/enabled');
    }

The template will look like the following way:

if($this->canShow()):?>
<form method="post" action="<?php echo $this->getUrl('cloudfront/index/flush'); ?>">
    <h3><?php echo __('Enter paths to files you want to invalidate on Amazon Cloudfront Side. Each new path you should specify from new line.'); ?></h3>
    <input type="hidden" name="form_key" value="<?php echo $this->getFormKey(); ?>"/>
    <textarea name="files" rows="10" style="width:100%"></textarea>
    <button type="submit" class="button">
        <span><span><?php echo __('Flush'); ?></span></span>
    </button>
</form>
<?php endif;?>

The form in the admin panel should have “form_key” element, otherwise, the form will not be processed. “Textarea” element allows the administrator to specify the ways to files, which caches should be cleaned up on Amazon CloudForm side. So, we just need to implement the controller. Let’s do it.

Implementing Controller for Processing the Form Submit in Magento 2

We can implement the controller in the following way:

class Flush extends \Magento\Backend\App\Action {

    public function execute(){
        $files = $this->getRequest()->getPostValue();
        $logger = $this->_objectManager->get('Psr\Log\LoggerInterface');
        try{
            if(!isset($files['files'])){
                $this->getMessageManager()->addError(__("Files haven't been provided"));
            }else{
                $files = explode("\n",$files['files']);
                foreach($files as $k=>$file){
                    if(trim($file)==''){
                        unset($files[$k]);
                    }else{
                        $files[$k] = trim($file);
                    }
                }
                if(!count($files)){
                    $this->getMessageManager()->addError(__("Files haven't been provided"));
                }else{
                    $scopeConfig = $this->_objectManager->get('\Magento\Framework\App\Config\ScopeConfigInterface');
                    $params = array('version' => 'latest',
                        'region'=>$scopeConfig->getValue('amazoncloudfront/general/amazon_region'),
                        'credentials' => new \Aws\Credentials\Credentials(
                            $scopeConfig->getValue('amazoncloudfront/general/access_key'),
                            $scopeConfig->getValue('amazoncloudfront/general/secret_access_key')));
                    $client = new \Aws\CloudFront\CloudFrontClient($params);
                    $storeManager = $this->_objectManager->get('Magento\Store\Model\StoreManager');
                    $distributions = array();
                    foreach($storeManager->getStores() as $store){
                        $distribution = $scopeConfig->getValue('amazoncloudfront/general/identifier','store',$store->getId());
                        if(!in_array($distribution,$distributions)){
                            $distributions[] = $distribution;
                        }
                    }
                    foreach($distributions as $distribution){
                        $requestArray = array('DistributionId'=>$distribution,
                                              'InvalidationBatch'=>array('CallerReference'=>time(),
                                                    "Paths"=>array('Items'=>$files,'Quantity'=>count($files))));
                        $res = $client->createInvalidation($requestArray);
                    }
                    $this->getMessageManager()->addSuccess(__('All paths have been invalidated'));
                }
            }
        }catch(\Exception $e){
            $this->getMessageManager()->addError($e->getMessage());
            $logger->error($e);
        }
       $this->getResponse()->setRedirect($this->_redirect->getRefererUrl());
    }
}

As you can see, this controller creates the object of the \Aws\CloudFront\CloudFrontClient type with the use of configuration parameters. It works the following way. The controller sends the request for cleaning cache following the ways which administrator entered to the input field of “textarea” type. This request is sent for each apart identifier. If some errors arise, for example, you have no rights on the Amazon side, or the way is not correct, the library will through the exclusion with a message. And the admin will see this message.

The ways in “textarea” must always begin with “/” symbol. For example: ‘/media’ or ‘/static’.

How to Hire Outsourced Web Developers: Best Practices Guide

Have you ever thought about hiring outsourcing services? Having enough internal resources for web development projects is great. But what if you can much more? What if you can increase the workflow without taking additional efforts?

Look…

Outsourcing web development solves a lot of problems related to resources, costs, and project deadlines if you work with people who care about your projects. Let us introduce the guide on how you can hire the professional outsourced web developers for your job.

Let’s keep the ball rolling!

Outsourcing Web Development Benefits and Risks

When we start thinking about the partnership with outsourcing company or hiring the outsourced web developer, we should keep in mind several pros and cons of outsourcing.

Want to know the key ones? – We are revealing all of them.

Why Outsource? The benefits of IT Outsourcing

There are a lot of process of web development outsourcing. And many of them you already know. Let’s recall them:

Getting more resources for clients’ projects. If you outsource some services, you’ll get more resources. Therefore your team can implement more projects. You can hire one or more outsourced web developers, and they will work as the members of your team. So, if you need more work resources for implementing some tasks and projects – this is the way.

Expansion of the brand’s expertise. You can do it by creating the B2B partner network. If your company expertise includes operating several technologies, you can partner with some outsourcing company which expertise includes some more required technologies. You can add these technologies to your competence. Then just outsource the projects to your partner. This helps to build a solid B2B partnership. Therefore, you can offer the wide range of services to your customers.

Cutting costs and saving time. This means that you can hire outsourced web developers who work on low or average hourly rates as compared to your company’s hourly rates. The difference in rates will help you to cut cost. As for time, you’ll have more time with your team for implementing the other clients’ projects.The business world is continually growing, competition is thriving, and it takes hard work and strategy to come out on top. The following outsourcing statistics shared over at Timedoctor will help you understand how big the outsourcing industry is, whether or not it’s truly worth your time, and how much you can potentially save your organization.

Some website outsourcing companies in the USA and other parts of the world take care of client’s time. They provide their clients with remote project managers. So you won’t need to worry about the outsourced projects at all. We, at WEB4PRO, take care of our partners, and they feel calm and happy 🙂

Pros of outsourcing are obvious. Nevertheless, there are several potential risks we should keep in mind.

How can you secure yourself from risks? First, read our guide up to the end.

Challenges of Web Development Outsourcing

Okay, the main risk we take when decide to outsource the project to web developers is that we don’t know exactly whom we are dealing with. In detail, we can describe these risks the following way:

The quality of final results may not meet our expectations. This is the pitfall which always stays in the way of any cooperation. Before choosing the web developer for your project, we should make sure that they are able to provide us with the qualitative results.

Well, so what is quality? – We’ll talk about this soon, keep reading!

The risk to be tricked by “not good” guys. This is a real world. Sometimes if you deal with no-name outsourced web developers, they can take your money and don’t complete the project. Or you’ll just get bad final results. If you want to secure yourself from this type of tricking, always choose web developers on the credible freelancing platforms. These platforms secure our payments and contracts, so there are no ways for tricks.

If we talk about companies, most of them sign the contract which secures both sides of a deal. So, always ask for the contract. Try to choose the companies which have a good reputation, qualitative portfolio, and corporate website.

Finally, when you are in the know of all benefits and disadvantages of web development outsourcing, it’s time to start the search for good web developers, right?

Are you ready to get the list of the best resources? – Let’s move on!

Where You Can Find Web Developers for Hiring

First of all, you need to know the places where you can post the job description, or even choose the people for your project from the ready-made list. But before you start searching, decide whether you want to work with the outsourcing company or freelance web developers. The approach to work processes will be different. Nevertheless, you can look through all the resources and make up your mind along the way.

Maybe now you are thinking about the list of resources… We have something for you.

There are a lot of platforms, catalogs, and professional social networks where the best outsourcing companies and freelance web developers have their profiles. You are welcome to use our list of these resources.

Check it out!

If you are looking for freelancers, here is a list of the best global freelancing platforms:

Well, but what about the outsourcing companies?

In this case, we recommend you to view the credible catalogs and platforms where you can find top web development companies in the world. Most of them provide dedicated teams of professionals and remote developers for your projects. And the cost for outsourcing can be very attractive. In most catalogs, you’ll find the hourly rate defined.

Here is the list of resources where you can choose web outsourcing companies in USA, Europe, and the other countries:

This list will help you to focus on the right places.So, when you finally find some good profiles which catch your eye, how will you choose the best web developer? First of all, we suggest you request the CV.

Let’s focus on the way of how we can choose the best developers for your job.

Choosing the Best Outsourced Web Developers

Considering outsourced web developer’s CV

There are the various types of developer’s CVs around the word. We think all of them should have some necessary parts to make the description of developer’s experience clear for a client. We prepared the points we think are the key in the CV.

Here they are:

Years of experience. Often the level of developer’s knowledge depends on this point. The more years of experience the web developer has, the higher his gradation level is (like Junior, Middle, or Senior). This level also affects the developer’s hourly rate value.

About information. This information should describe the basic specialist’s competence and their professional skills.

Expert skills. This point should define main technologies and tasks in which the specialist has an expertise. Pay attention to this point.

Technical expertise. This section provides the detailed list of technologies, tools, and case studies with which the web developer worked. You can find lots of useful information here. For instance, if most of this list is what your project requires, you can continue communication with the developer.

Professional experience. It usually explains the last and current web developer’s responsibilities. From the other hand, you can find the information about job post and career growth. It’s useful if you are looking for some additional personal skills, like management, team leading, or vanity.

Work examples. We are sure that work examples should be represented not only in the form of links to the websites created. Furthermore, the web developer should describe technologies used on these projects. A well-made portfolio should consist of qualitative, modern, and complicated work examples.

Languages proficiency level. It will be the pros if the web developer reveals English proficiency level. This will help you to understand whether it is convenient to have a talk with each other.

Education. It’s not the key point which will influence your choice. But, definitely, the specialists who obtained the higher education in mathematics or computer science has more chances to cope with non-standard tasks.We can extend the item list in the CV. Some creative people can add something more personal, like a life motto or a view of life 🙂

The main purpose of developer’s CV is to provide the client with all necessary information. Having this information will help you to choose the person with the required level of competences.

Great! We have several good CVs. So, what then? Let’s choose your champion.

Evaluating the Quality of Web Developer’s Work

Before we move to the final step of our trip, let’s define what the quality is exactly for us.

In web development, the quality is a clear code structure, ability to modify this code, the working final solution, and, yes, the high level of communicational and management skills. These are the essentials of successful cooperation.

How can you check the quality?

Don’t hesitate to ask the developer for examples of works. Offer them to complete the test task. Also, you must contact the technical interview to understand the psycho type of the person you are going to work with. It will help understand whether you both can easily cooperate with each other and inside your team.

Conducting the Technical Interview

outsourced web developer's technical interview

If you know what technical skills the web developer should have, you can pick the best value for your money.

How to conduct the technical interview?

The best way to do this is to prepare the list of questions related to the technical side of your topic. Also, ask the web developer about their experience, tools, and technologies they worked with, and ask about examples of cases.

Who can take part in the technical interview?

The necessary person of this process is your technical team leader, who is the technical specialist from your side, and the product owner. The product owner is a role of the key person on the project. It’s a client, in other words. You or another responsible person from your side can be the product owner. Together with the technical specialist, you’ll carry out the professional interview. Both of you can focus on different parts of future cooperation.

What services can you use for the interview?

As now we usually partner with the web developers from all parts of the world, choose the credible services which provide the good quality of connection and ability to make video calls:

  • Skype
  • FaceTime
  • Viber

What should you pay attention to?

We suggest you focus on the following:

  • the level of technical knowledge;
  • web developer’s communication skills;
  • English level (whether you understand each other or not):
  • ability to adherence the timeframes;
  • ability to recommend some custom and creative solutions.

That’s all. This may help you to conduct the successful technical interview.

Now you are almost ready to choose the best Outsourced web developer.

But!

Let’s add some more thoughts….

Several Necessary Steps Before You Start the Project

Before you start, make sure that you are secured from risk.

Check the remote web developer’s work environment. That means you should wonder what tools and equipment web developers use for doing their daily tasks. Do they have the allocated workplace with all needed staff for their convenient work? If they do, that means your team will cooperate easily, and they will be focused on a project. The work will be much more productive.

Sign the contract. Each deal with money should be fixed in black and white. This secures both sides from risks. Also, you can request signing NDA and ask for preserving the intellectual property right.

In conclusion, when you think that you want to work with the person in front of you, just do it! Don’t worry too much about risks. Outsourcing is a popular and effective approach now. It tends to help both partners gain the income and grow their business.

Finally, let’s recall all steps we should make for choosing the best remote web developer.

We should do the following when hiring the outsourced web developer:

  • get aware of benefits and risks of IT outsourcing;
  • define the list of trusted web resources for posting job offers and choosing the companies;
  • consider the well-structured and relevant web developers’ CV’s;
  • evaluate the potential of web developer’s work quality;
  • carry out the professional technical interview;
  • ask for guarantees and contract;
  • choose the best person for your job!

Thank you for reading. We tend to take care of everyone who needs our help. That’s why we want to be useful even on the stage of choosing the specialists. Yes, the project start can be easy! Just give it a try and drop us a line if this guide works for you.

WEB4PRO Clients Speak Up on Clutch

Nothing beats the feeling of delivering a finished product to a client. At WEB4PRO, our creative and experienced team loves our clients, and we love delivering solutions to them that work. With over a decade of experience (and clients who have been with us since the beginning), we’re proud of the results our clients have seen through our lines of code. Recently, our clients have let us know just how much they appreciate us by speaking up on Clutch.Clutch is a ratings and reviews platform based in the United States that focuses on helping business buyers make confident purchasing decisions. Their platform includes several thousand development, design, and marketing companies from around the globe. Using a proprietary research methodology, Clutch examines each of these firms to determine their ability to deliver great results through their work.

Alongside Clutch’s complex methodology, their team relies heavily on client interviews. Analysts at Clutch spend 15 minutes on the phone with clients asking a series of questions aimed at the companies’ services offered, results of the engagements, and project management styles.

We’re extremely proud that WEB4PRO’s clients have shown their appreciation for our work in their Clutch reviews. Here are some of the highlights from those conversations:

“They’re excellent. They always have a project manager who is very friendly, professional, courteous, and open to new ideas if needed for an upcoming build. We really enjoy working with their project manager.”

“They’re very reliable and very talented. We know that if we hand a project off to them, they will provide an estimate down to each line item on which they’ll spend time…. They’re very friendly, courteous, and professional. They do an amazing job. There’s nothing we’ve asked them to do that they have balked at, so we really enjoy working with them.”

“I have developed a sense of trust with WEB4PRO. We switched from working on a fixed-price basis to by the hour. I give them tasks and know that they’ll charge a fair price. I trust them enough to have an open-ended setup.”

We put the care about our clients and partners first in our cooperation process. That’s why we’re incredibly grateful for the trust our clients have placed in our team, and we’re proud of the strong ongoing relationships that we hold with some of our closest partners.

To find out more about what these partners have to say about us, check out our full Clutch profile.

Using Negative Values for the Product Quantity in Magento 2

When we deal with product quantity in Magento® 2, and it’s less than zero, we face the challenge. Sometimes we need to order the products in the online stores when they are not available at the moment. These products are in stock in Magento 2 but Merchants order them from the manufacturers and shippers after we place the order. In eCommerce, we call this back-order or drop-ship. In this case, the number of product quantity in the online store inventory can be less than 0. Yet, the products are available for the ordering.

Magento 2 includes this functionality. We can turn on the support for Qty field values which are less than 0 in Inventory Management Configuration. But there is one small catch. If the product quantity in Magento 2 is less than zero, the online shop administrator can’t edit and resave this product. The following error will appear in Qty field:

“Please enter a valid number in this field.”

You can find the description of this issue and the possible solution to it on GitHub. But you should know that it doesn’t work on the latest Magento versions (checked on Magento 2.1.7). We’ll cover the reason for this issues and the solution to it below.

The product editing form is implemented with UI-component. It is useful in case we need to do customization with different modules. For example, the form is claimed in the Catalog module. However, Cataloginventory module handles the quantity management.

Cataloginventory Module for Product Quantity in Magento 2

It’s possible to implement customization with classes-modifiers. They fit into Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool virtual type with di.xml.

If we take the Cataloginventory module, this modifier is claimed in the following way there:

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
        <arguments>
            <argument name="modifiers" xsi:type="array">
                <item name="advancedInventory" xsi:type="array">
                    <item name="class" xsi:type="string">Magento\CatalogInventory\Ui\DataProvider\Product\Form\Modifier\AdvancedInventory</item>
                    <item name="sortOrder" xsi:type="number">20</item>
                </item>
            </argument>
        </arguments>
    </virtualType>

The modifier class should implement \Magento\Ui\DataProvider\Modifier\ModifierInterface which must create two methods: modifyData($data) and modifyMeta($meta).

The component configuration is implemented in modifyMeta($meta) method, including its frontend part (javascript). In the solution from GitHub, the developer recommended turning off the digital validation with a custom modifier. We can implement it in the following way:

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
        <arguments>
            <argument name="modifiers" xsi:type="array">
                <item name="hf_quantity" xsi:type="array">
                    <item name="class" xsi:type="string">Web4pro\Defaultproduct\Ui\DataProvider\Product\Modifier\Quantity</item>
                    <item name="sortOrder" xsi:type="number">10000000</item>
                </item>
            </argument>
        </arguments>
    </virtualType>
class Quantity implements \Magento\Ui\DataProvider\Modifier\ModifierInterface {

    protected $arrayManager;

    public function __construct(\Magento\Framework\Stdlib\ArrayManager $arrayManager){
        $this->arrayManager =$arrayManager;
    }

public function modifyMeta(array $meta)
{
    if ($path = $this->arrayManager->findPath('quantity_and_stock_status_qty', $meta, null, 'children')) {
        $this->arrayManager->remove(
            $path . '/children/qty/arguments/data/config/validation/validate-digits',
            $meta
        );
    }

    if ($path = $this->arrayManager->findPath('advanced_inventory_modal', $meta)) {
        $meta = $this->arrayManager->merge(
            $path . '/children/stock_data/children/qty/arguments/data/config',
            $meta,
            ['validation' => ['validate-digits' => false]]
        );
    }

    return $meta;
}


    public function modifyData(array $data){
        return $data;
    }
}

In the latest Magento versions (Magento 2.1.7) this code will not work. The reason is the following. Magento has the implementation of support for integers and fractions. For example, the fractions work in case of product drop-shipping by weight.

The Solution for Product Quantity in Magento 2

Magento_CatalogInventory/js/components/qty-validator-changer component was implemented for the quantity validation. This is its code:

define([
    'Magento_Ui/js/form/element/abstract'
], function (Abstract) {
    'use strict';

    return Abstract.extend({
        defaults: {
            valueUpdate: 'input'
        },

        /**
         * Change validator
         */
        handleChanges: function (value) {
            var isDigits = value !== 1;

            this.validation['validate-number'] = !isDigits;
            this.validation['validate-digits'] = isDigits;
            this.validation['less-than-equals-to'] = isDigits ? 99999999 : 99999999.9999;
            this.validate();
        }
    });
});

handleChanges method checks the configuration value transmitted to the component. If the integers are used for the quantity, we should apply validate-digits validator. If we deal with fractions, we should apply validate-number. So, it’s not enough to turn off the validate-digits validator. We also should set the needed value of handleChanges function’s parameter. It allows us to avoid choosing this validator by the other function.

We describe this function’s parameter in Magento\CatalogInventory\Ui\DataProvider\Product\Form\Modifier\AdvancedInventory modifier the following way:

...
'imports' => [
                    'handleChanges' => '${$.provider}:data.product.stock_data.is_qty_decimal',
                ],
.

As we can see, we can get its values in both cases, whether we use integers or fractions for quantity. We set that in the configuration. So, let’s implement modifyMeta($meta) method in our modifier:

  public function modifyMeta(array $meta)
    {
        if ($path = $this->arrayManager->findPath('quantity_and_stock_status_qty', $meta, null, 'children')) {
            $this->arrayManager->remove(
                $path . '/children/qty/arguments/data/config/validation/validate-digits',
                $meta
            );
            $this->arrayManager->merge($path . '/children/qty/arguments/data/config/imports',$meta,
                                       array('handleChanges'=>"1"));
        }

        if ($path = $this->arrayManager->findPath('advanced_inventory_modal', $meta)) {
            $meta = $this->arrayManager->merge(
                $path . '/children/stock_data/children/qty/arguments/data/config',
                $meta,
                ['validation' => ['validate-digits' => false],'imports'=>['handleChanges'=>'1']]
            );
        }

        return $meta;
    }

Finally, we have the next results:

  • we turned off the validate-digits validator;
  • we set ‘handleChanges’ equal to 1.

It will cause the use of the right frontend validator. Furthermore, it won’t break the backend logic for the products which are on sales by item not by weight. We should repeat this twice because we can find the link under the Qty field. This link opens the advanced_inventory_modal pop-up.

As a result, the online store administrator will be able to save the products of which quantity is less than 0 without any problems.

This case is new to Magento 2. We make everything possible to make your journey with Magento 2 easier. If you have any questions regarding Magento 2 Extension Development, please, contact us. We’ll take care of your project.

How to Choose the Best Magento Outsourcing Company

Web development outsourcing is quite a popular approach among IT businesses and digital agencies. It is a good way to get more resources, manage the large work scope, and cut down the costs. Outsourcing turns out to be one of the best modern B2B practices. We can find a lot of web development firms and agencies. But what exactly can help us to make the right choice? Say, you are looking for a good Magento® development outsourcing company. Will it be more complicated to select the executors? Should you pay attention to some specific requirements?

The answer is yes! Let’s consider the key points you need to know to choose the professional Magento outsourcing company for your job.

As an experienced outsourcing web development team, we are aware of the clients’ main requirements to us. Moreover, we succeeded to cooperate with several large companies for 10 years. Keeping this information in mind, we managed to create the guide for entrepreneurs who decided to outsource their Magento projects. So, how to choose the best Magento development outsourcing company?

Create a Portrait of the Perfect Magento Outsourcing Company

It is not a joke. This step will help you to come up with your requirements for the company.There are the things you should think about before starting your search:

Language. What language do the employees speak? It’s a crucial moment for starting communication with your future partner. Definitely, you need to understand people with which you are going to cooperate. For example, if your team speaks English, it’s a good idea to choose the partner among the English-speaking companies.

Time Zone. Another good idea is to determine the time zone you would like your partner’s office to be situated. The closer their time zone is, the better. For, example, we are in the 3 GMT time zone. Our clients are from America, Australia, Europe, UK. Our time difference is up to 12 hours. However, our team works with clients from different time zones.

Project Area. When you are going to outsource your Magento project, surely you should know the area it covers. For example, you want to hire Magento 2 developers. Not all Magento companies work with Magento 2. So you can narrow the choice down. Or, you want to implement some custom features that require the creative approach. It means you should focus on this factor while talking to candidates.

The Number of Developers. Knowing the number of Magento developers required for your project will help you to calculate your budget. Additionally, this allows you to find the companies which provide the wished number of development resources available right at the moment.

Developer’s Skill Set. The main thing you should focus on is the required skill set for Magento developers you are going to hire. We’ll not dig too much into the basic technical knowledge right now. But think about the level of web developers you would like to hire. The level could be Middle, Middle , Senior, Magento Certified Developer. Sometimes Junior Magento developers can cope with some simple tasks. The specialists’ hourly rates depend on developers’ level. So we recommend you to come up with the wished skill set before starting cooperation. It allows avoiding unnecessary money spending.

Maximum Hourly Rate. Determine the maximum cost you are willing to pay for the project. It’s a good idea to calculate the budget. You could find the perfect company you’d like to cooperate with. However, their hourly rates may appear too high for you. Although if the company meets your requirements and provides the qualitative results, you may agree with their price.

When your portrait is ready, you can start your research. Just keep in mind the things you figured out before.

Search For Magento Development Outsourcing Company

Searching for Magento web development company is an obvious step. When you have the complete portrait of your ideal executor, it’s much easier to find the company. You should know where to search. These are the several ideas about the best places from which you can start.

Google. Okay, Google, let’s find the dream team for our project. Most of the companies have good websites. Of course, you’ll find some websites in the top search results. Don’t hurry. Visit the websites which appear farther from the top of Google’s search results. SEO is a large part of marketing strategy. However, you are looking for the web developers, right? The main things you should pay attention to are the company’s website quality and the information provided there. Try to feel whether their story sounds honest enough for you or not.

Another interesting moment you can focus on is the company’s virtual life. This is the way how it works. Check whether the company has social media profiles, Google profile, Google my Business account. Answer the following questions. Can you see their team on Instagram? Do they have a blog which proves their expertise? Does their office appear on Google maps? Do they show their experience on Quora, social networks, Medium? Do they have their products presented on Magento Connect or Magento Marketplace? If the answer is yes for most of these questions, you are on the right way. You should know the people with which you are going to work.

B2B research catalogs. It is one of the best ways to find a good Magento development company. Why? The catalogs allow setting filters. They save our time. Say, you are looking for the UK Magento development company. Tick this option, and then you’ll see the results only for the UK. Filtration by minimum project budget, company size, and the other options is also available. Also, companies’ profiles go through the multilevel verification process. The same happens to the reviews posted there by the clients. Take a look at famous B2B research catalogs, such as Clutch, GoodFirms, TopAgency, CrunchBase, Glassdoor. You can find a broad choice of opportunities there.

Commerce Hero. Commerce Hero is a platform for connecting web development specialists in eCommerce. There we can find Magento developers’ and companies’ profiles. This platform also allows each member to add the portfolio and post the articles. So look through the profiles and connect with your heroes.

Magento Partners. If you are looking for a B2B partner that must also be the official Magento partner, you must get there. Go to Magento partners directory and see all you need to know about your possible partners.

Linkedin. It’s a social network for professionals. It connects people and helps them to build business relationships. On Linkedin, you can find companies, the job of your dream, and a lot of professional people from different knowledge areas. Linkedin is a right place for networking and setting business relationships. You can get in touch with companies’ CEO, directors, and managers directly there.

Meetups and Conferences. No doubts that one of the best ways to find the partner is attending Magento conferences, expos, and meetups. At these events, people share the experience and communicate. They try to know each other better. If it works for you, consider these events like adventures and future opportunities.

Make the Final Choice: Pay Attention to Detail

Finally, you have the portrait of your ideal Magento development company, you planned your budget, and you have the several options you found. It’s time to make the final choice. So, you start communication with companies. We have some extra points you can focus on before choosing the best company. Check them out:

Code Standards. Everybody says their work is qualitative. What do they mean exactly? How can we evaluate the quality of a web developer’s work? There are three parts of good results that should work together: clean code, creative approach, and adhering to deadlines. The company should follow official code standards to perform the best result. Their code should be easy-to-modify. You can ask for developers’ code examples on Github.

Business Processes. An effective process is an essential tool for fruitful cooperation. Don’t hesitate to request the detail description of management processes. It’s good if the company has some kind of business proposal or even a PlayBook. They should tell about the project management model they follow. Are they ready to adjust to your processes? Are their processes convenient for you? Try to pay attention to this moment.

Magento Developers. Of course, you started this everything in order to find professional Magento developers. Good reliable companies have Magento certified developers on their team. Magento Certificate proves that the web developer passed official Magento exam. It shows developers’ expertise in Magento products. Also, take a look at CVs which the company offers. Compare the skill set with your wished one. Personal interview with the candidates will help you to make a final decision.

Portfolio and reviews. The works prove the team’s skills and expertise. A professional portfolio should consist of complicated, beautiful, and modern websites. Another extra safety measure is checking the clients’ reviews on sources like Clutch, Linkedin, Facebook, Google. You’ll see the clients’ true stories there. Before these reviews are posted, the client provides the personal details. It’s a complicated verification process. There are no chances to trick the system.

Guarantees. The guarantees will help you to stay confident and secured. Ask what guarantees the company can provide you with. Do they sign NDA or some other contract in black and white? It’s good if the company guarantees the substitute if their developer leaves the project. We, for example, provide a free trial for each our web developer. It helps our clients understand whether our people are able to cope with their job.

The Guide on How to Speed Up WordPress

Dou you want to speed up WordPress?

Today we’d like to share our guide on how you can make your WordPress website much faster. WordPress is quite a user-friendly and easy-to-run CMS, it should also have the high performance.

If you own the WordPress based website, we recommend you to check whether it’s optimized or not. Present and quick-coming future require the high performance of any process. The users expect more stability and efficiency from websites they visit. Time is the valuable thing now, and even several seconds of the user’s waiting can cause the falling of involvement and visiting of your website. So we need to check the things and fix the problems.

Let’s do it!

First, run Google’s speed test. You’ll see the level of website performance. If you see the red color on a scale, your website needs increasing. You can also check your website with Pingdom. It shows you the state of your website and gives the recommendations.

Then you can speed up WordPress performance following our quick guide a checklist. There you’ll find the basic things that influence page loading time of WordPress website. Let’s begin!

5 Basic Steps to Speed Up WordPress

Here are several most important things you can do to speed up your website.

Minimize CSS and JS Files

Here we talk about the code optimization. The less amount of code your web page has, the faster it’s loaded. The same goes for content. We’ll talk about it later. So, try to write the clean code, and it will help you to keep your WordPress website quick.

You can do it using W3 Total Cache Plugin. This is a pretty helpful plugin in the whole process of WordPress performance optimization and has many advanced features.It includes a built-in tool for minifying CSS and JS file.

Here are the things you should do:

  • install W3 Total Cache Plugin;
  • run it and find Minifying Setting in General Setting Tab;
  • enable Minify in a checkbox and save your changes.

Analogically, you can use such plugin as Autoptimize. You should go to the Settings Page after its installation set the following items in the checkboxes:

  • Optimize HTML code;
  • Optimize CSS code;
  • Optimize JavaScript code;
  • Generate data: URLs for images.

Then save your changes.

Also, you can use the online services like:

Don’t forget to check your website speed again after each step.

Optimize Images

Images take up the most part of things that are loaded on your website. It’s better to compress them and reduce the server load.

You can do it with a help of EWWW Image Optimize plugin. It automatically optimizes images you upload and also can work with already uploaded images.

Also, we recommend you to install Lazy Load plugin. It loads the images only when they are visible to the user that reduces the server load in many times.

Optimize Database

When you use WordPress for a while, the database stores much information which is already unnecessary. We mean, some tags and categories, images you don’t use, trash posts, etc.

You can clean up your database using the WP-Sweep plugin.

Use Caching for Better Performance

You should know that caching help to reduce the server load drastically. Cache store the copies of already loaded pages by the user, and provides him with them. So it increases website speed. You can enable caching via W3 Total Cache Plugin. You’ll find all features for caching in General Settings Tab. We recommend you to use caching and take the advantages of it.

Enable GZIP Compression

GZIP compression helps to build pages on a server before the web page is sent to the user. It increases the website performance drastically. You can enable it with W3 Total Cache.

There were 5 common recommendations on how to improve WordPress website performance. Also, try to keep WordPress updated and choose fast and reliable hosting.

Each website should tend to not more than 3 seconds of loading time according to the research. It influences your conversions. Take a look: How page loading speed influences your conversion rate.

Remember, when your website is loaded fastly, and the visitors don’t need to wait, you take care of their time.

Now, when you’ve tried all our recommendations, run the speed test, and if you need more improvements, contact us.

We’ll be glad to help you!

Extension Attributes for Total Model in Magento 2

We continue to set extension attributes in Magento® 2. Last time we were talking about the adding a custom attribute to the customer’s address. But what if we need to set these attributes for the total model? Today we’ll consider how we can do that step by step.

Total Model

Total model is a dynamic system used for collecting the shopping cart and the order total. It usually contains the elements which take part in calculating the total cost: final products total, discount, tax, shipping fee, and some other parameters.

How It Works

Imagine the online store. You buy some products there and add them to the shopping cart. When you view the shopping cart page, you can see something like this:

Subtotal – X.XX$ final cost of products (the product’s cost multiplied by the number of products)

Tax  – X.XX$ (optionally)

Shipping  – X.XX$ (optionally)

Discount (optionally)

Total – X.XX $ (the final cost of the order)

Let’s take a look at VENROY online store (it’s Magento based) to see how it can work for you:

Magento based online store

The whole system shown in this example is the Total model. Let’s move on.

What We Need to Do

First of all, we need to add the row before subtotal in the total model. The row will show the customer’s profit. The profit is a marketing element that shows how much money the customer saves using the offer. This profit should not be included in the final cost of the shopping cart and the order totals. It is calculated on the base of subtotal.

So, we can separate our task into two main problems:

  • Add the row before Subtotal and don’t include its results to the total cost.
  • Make the extension and Magento 2 work right together.

These both conditions must work correctly with each other.

In the end, we should get the result of this kind:

You save: (Our additional row)

Subtotal – final cost of products (the product cost * the number of products)

Tax (optionally)

Shipping (optionally)

Discount (optionally)

Total – the final cost of the order

Let’s go on to code!

Preconditions

There is a Magento 2 based online store. It is oriented on the retail and wholesale customers. Due to this fact, most of the products have the tier prices, as well as the retail prices. The types of store products are the simple products and configurable products. As we said before, we need to output the additional row before Subtotal on the shopping cart and the order pages. This row must show the total profit for the wholesale customer. While calculating the profit, we don’t include the extra-charge for product options. This row mustn’t influence the order final cost, as well as it mustn’t be copied to the order.

Solution

Setting Magento 2 Extension Attributes

Last time, we added the custom attribute to the customer address. Let’s follow the same way. We’ll add the style_discount field with decimal(10,4) type to the address extension attributes table from the last example. Also, we’ll need to add the attribute to the extenson_attributes.xml file of our module. We should add it twice for two different interfaces.

...
<extension_attributes for="MagentoQuoteApiDataAddressInterface">
        <attribute code="type" type="int">
            <join reference_table="web4pro_quote_address" join_on_field="address_id" reference_field="address_id">
                <field column="type">type</field>
            </join>
        </attribute>
        <attribute code="style_discount" type="float">
            <join reference_table="web4pro_quote_address" join_on_field="address_id" reference_field="address_id">
                <field column="style_discount">style_discount</field>
            </join>
        </attribute>
    </extension_attributes>
    <extension_attributes for="MagentoQuoteApiDataTotalsInterface">
        <attribute code="style_discount" type="float"/>
    </extension_attributes>

In the first case, we should specify the table which will store this attribute. In the second case, there is no necessity to do this, because the entity implemented with MagentoQuoteApiDataTotalsInterface is always formed from the address data along the way. Saving of this attribute will be performed in sales_quote_address_save_after event processor, which we described in the previous example.

Furthermore, we need to describe the total model. Let’s create an etc/sales.xml file in the module for these purposes. It looks like the following:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd">
    <section name="quote">
        <group name="totals">
            <item name="style_discount" instance="Web4proAjaxcartModelQuoteAddressStyle" sort_order="110"/>
        </group>
    </section>
</config>

We choose the sort_order parameter keeping in mind that calculations were performed after Subtotal had been calculated. In this case, the Product entity has already been initialized for all shopping cart elements.

Implementing Total Model in Magento 2

The total model must implement two methods: collect() method used for the calculation and fetch() method used for the output on request. You can implement the total by following this example:

namespace Web4proAjaxcartModelQuoteAddress;
use MagentoQuoteModelQuoteAddressItem as AddressItem;

class Style extends MagentoQuoteModelQuoteAddressTotalAbstractTotal {

    protected $_objectManager;

    public function __construct(MagentoFrameworkObjectManagerInterface $objectManagerInterface){
        $this->_objectManager = $objectManagerInterface;
    }

    public function collect(
        MagentoQuoteModelQuote $quote,
        MagentoQuoteApiDataShippingAssignmentInterface $shippingAssignment,
        MagentoQuoteModelQuoteAddressTotal $total
    ) {
        parent::collect($quote,$shippingAssignment,$total);
        $items = $shippingAssignment->getItems();
        $address = $shippingAssignment->getShipping()->getAddress();
        $amount = 0;
        foreach($items as $item){
            if ($item->getParentItem()) {
                continue;
            }
            if ($item instanceof AddressItem) {
                $quoteItem = $item->getAddress()->getQuote()->getItemById($item
->getQuoteItemId());
            } else {
                $quoteItem = $item;
            }
            $product = $quoteItem->getData('product');
            $childProduct = $product;
            if($product->getTypeId()=='configurable'){
                $childProduct = $product->getCustomOption('simple_product')->getProduct();
            }
            $qty = $quoteItem->getQty();
            $amount =(float)($qty*($childProduct->getPriceModel()->getFinalPrice($qty,$childProduct)-$childProduct->getPriceModel()->getBasePrice($childProduct,1)));
        }
        $total->addTotalAmount($this->getCode(),$amount);
        $extensionAttr = $address->getExtensionAttributes();
        if(!$extensionAttr){
            $extensionAttr = $this->_objectManager
->create('MagentoQuoteApiDataAddressExtension');
        }
        $extensionAttr->setData($this->getCode(),$total->getTotalAmount($this->getCode()));
        $address->setExtensionAttributes($extensionAttr);
        return $this;
    }
    public function fetch(MagentoQuoteModelQuote $quote, MagentoQuoteModelQuoteAddressTotal $total)
    {
        $result = null;
        $amount = $total->getTotalAmount($this->getCode());

        if ($amount != 0) {
            $result = [
                'code' => $this->getCode(),
                'title' => __('Style Discount'),
                'value' => $amount
            ];
        }
        return $result;
    }
}

Copying Data to Total Model in Magento 2

We need to provide copying the data from the address to the total model. It’s possible with the plugin from the previous example to MagentoFrameworkApiDataObjectHelper class. But note that we should work separately with address attributes and total model attributes. We copy the first ones to the order address and the second ones – to the total model. When trying to copy some data to the receiver which can’t receive the attribute, the exception will be thrown out. The method looks like this one:

public function beforePopulateWithArray($helper,$dataObject, array $data, $interfaceName){
        switch($interfaceName){
            case 'MagentoSalesApiDataOrderAddressInterface':
                if($data['extension_attributes'] instanceof MagentoQuoteApiDataAddressExtensionInterface){
                    $data['extension_attributes'] = $data['extension_attributes']->__toArray();
                    if(isset($data['extension_attributes']['style_discount'])){
                        unset($data['extension_attributes']['style_discount']);
                    }
                }
            break;
            case 'MagentoCustomerApiDataAddressInterface':
                if(isset($data['extension_attributes'])&&($data['extension_attributes'] instanceof MagentoQuoteApiDataAddressExtensionInterface)){
                    $data['extension_attributes'] = $data['extension_attributes']->__toArray();
                    if(isset($data['extension_attributes']['type'])){
                        $data['type'] = $data['extension_attributes']['type'];
                    }
                }
                break;
            case 'MagentoQuoteApiDataTotalsInterface':
                if($data['extension_attributes'] instanceof MagentoFrameworkApiAbstractSimpleObject){
                    $data['extension_attributes'] = $data['extension_attributes']->__toArray();
                    if(isset($data['extension_attributes']['type'])){
                        unset($data['extension_attributes']['type']);
                    }
                }
                break;
        }
        return array($dataObject,$data,$interfaceName);
    }

Outputting Total on Shopping Cart Page

Now we are going to output a new Total on the shopping cart page. It requires us to describe and implement Magento 2 knockout-component. It’s described in Layout file the next way:

<referenceBlock name="checkout.cart.totals">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="block-totals" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="style_discount" xsi:type="array">
                                    <item name="component"  xsi:type="string">Web4pro_Ajaxcart/js/style</item>
                                    <item name="sortOrder" xsi:type="string">1</item>
                                    <item name="config" xsi:type="array">
                                        <item name="template" xsi:type="string">Web4pro_Ajaxcart/checkout/cart/totals/style</item>
                                        <item name="title" xsi:type="string" translate="true"><![CDATA[Style Quantity Discount]]></item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>

We choose sort_order taking into consideration that the row is outputted before Subtotal. As you can see, the component consists of a template and a javascript file. The template is loaded asynchronously, and it’s filled with the content with JavaScript. This is how the template supposed to look like:

<tr class="totals">
    <th class="mark" scope="row" data-bind="text: title"></th>
    <td data-bind="attr: {'data-th': title}" class="amount">
        <span class="price" data-bind="text: getValue()"></span>
    </td>
</tr>

JavaScript code:

define(
    [
        'Magento_Checkout/js/view/summary/abstract-total',
        'Magento_Checkout/js/model/quote'
    ],
    function (Component, quote) {
        "use strict";
        return Component.extend({

            isDisplayed: function() {
                return this.getPureValue()!=0;
            },
            getPureValue: function() {
                var totals = quote.getTotals()();
                if (totals) {
                    if(typeof totals.style_discount=='undefined'){
                        quote.setTotals(window.checkoutConfig.totalsData);
                        totals = quote.getTotals()();
                    }
                    return totals.style_discount;
                }
                return quote.style_discount;
            },
            getValue: function() {
                    return this.getFormattedPrice(this.getPureValue());
            }
         });
    }
);

Pay attention to the if condition (type of totals.style_discount==’undefined’). It’s necessary because Magento 2 extension attributes (we checked  2.1.2 version) are written to Total JavaScript object if only the asynchronous loading takes place (_SetTotals() method). It is not being written when the page is loaded from window.checkoutConfig.totalsData.

We can output this component to the order page by following the same routine.

Pitfalls

One of the moments we should keep in mind is that Total content is being added while calculating the Grand Total. It’s not good for our task. The reason lies in the implementation features of Grand Total calculation in MagentoQuoteModelQuoteAddressTotalGrand class. Take a look:

public function collect(
        MagentoQuoteModelQuote $quote,
        MagentoQuoteApiDataShippingAssignmentInterface $shippingAssignment,
        MagentoQuoteModelQuoteAddressTotal $total
    ) {
        $totals = array_sum($total->getAllTotalAmounts());
        $baseTotals = array_sum($total->getAllBaseTotalAmounts());

        $total->setGrandTotal($totals);
        $total->setBaseGrandTotal($baseTotals);
        return $this;
    }

We can fix this out using a plugin which excludes our total from the sum.

<type name="MagentoQuoteModelQuoteAddressTotal">
        <plugin name="web4pro-remove-external-totals" type="Web4proAjaxcartModelPlugin" sortOrder="20"/>
    </type>

public function afterGetAllTotalAmounts($total,$result){
        if(isset($result['style_discount'])){
            unset($result['style_discount']);
        }
        return $result;
    }

Conclusion

Finally, we have coped with our task. Let’s summarise what we needed to do:

  • add the style_discount field with decimal(10,4) type to the address extension attributes table;
  • add the attribute to the extenson_attributes.xml file of our module (twice for two different interfaces);
  • create the etc/sales.xml file in the module to describe the total model;
  • implement the total model;
  • provide copying the data from the address to the total model with the plugin (work separately with address attributes and total model attributes);
  • fix the adding of Total content while calculating the Grand Total with a special plugin;
  • output a new Total on the shopping cart and the order pages.

This is how it works. We hope that our article answers your question about set extension attributes for the total model. However, if you have some issues regarding extensions at your store and you need assistance, we can provide you with Magento 2 Extension Development.

Wish you good luck with Total Models in Magento 2!

How to Understand Clients: Step into Their Shoes

You’ve been working at the position of Project Manager for five years. You think you understand clients and all working processes. You follow the rule: “Think like a customer. Feel the pains of the customer”. But much as you tried, you are still the manager, who acts using your company’s working templates.

I have been working at WEB4PRO since 2012. We have succeeded to complete over 20 projects from scratch since then. Now I have taken a maternity leave. So, I’ve got the time for starting the own project. This is my story about how it was.

Step Into Client’s Shoes

My project is a medical website from scratch. I made up my mind to bring it to life, and I began with the preparation. First, I created mockups and short project requirements with the detailed description. Then I sent these data to WEB4PRO for the estimation, as I completely trust my team. I know they follow the timeline and provide the high-quality result.

But… “What if I look up some market offers? What about checking some other companies? Do they understand client’s business and needs?” – I thought.

So I got down to business.

Follow the Client’s Routine

I sent a lot of requests for quote. We started communication with 11 contacts. There were various executors: company representatives and freelancers. I prefer working with companies. The company could cover the risks when some unexpected problems arise. Furthermore, a company can provide a complex solution. I decided to compare all candidates by several aspects.

Proofs. Portfolio

We considered a lot of offers. We focused on two things along the way: portfolio and the quality of works there.

I evaluated the portfolio by the websites it included. The most criteria for we were the websites design, compatibility, mobile versions, complexity. Also, I tested the main features. I declined the companies if their portfolio contained bad-made websites. Those websites were too old, too simple, unattractive, inconvenient. But the high-quality works caught my eye even if it was not relevant to the medical field. The relevancy was not the main factor for me. If the company built a great website, it could do a good job for us. So, I came up with the following:

The example of work may not be from the client’s business area. That’s great if it has some features like the customer’s project does (even if it’s a contact form functionality). The good portfolio must consist of complicated, modern, and beautiful works.

First Interaction

First, I provided the companies with links to mockups and the project specification. I requested the project estimation, hourly rate, and a technical solution. I wanted to check their level of understanding the client’s requirements. The result was curious.

Three people gave the estimation right on the same day, on Saturday. According to WEB4PRO standards, we provide the estimation within 3 working days. I knew the work scope required for our project. That’s why costs and terms the candidates suggested looked strange for me. They seemed too short.

Many candidates revealed their hourly rate. Also, they provided the project duration expressed in months. So, I couldn’t understand the final cost. Project duration expressed in months doesn’t make any sense. It’s much better to express it in hours. When we multiply the project term on the hourly rate, we get the final cost.

In conclusion, I declined several candidates for the following reasons:

  • improbable estimation;
  • the absence of related questions;
  • inattentive learning of project requirements;
  • unclear estimation expressed in months;
  • low level of communication.

Most freelancers replied within two days. They all suggested we contact via Skype and discuss the detail.

Due to all these facts above, I made the next observation:

When you first interact with the client, you set the first contact. It’s very important to understand customers needs and wants right on the first stages. They’d like to hear the questions related to their project. Also, always give the project duration expressed in hours, not in months or something else. Costs must be easy to calculate. It shows your honesty, and it saves clients’ time.

Skype Call

I talked to five candidates. I didn’t like the ones who asked me the odd questions. I understood they were not ready for negotiations. Also, the candidates who didn’t have any creative approach were not the right people for my job. Definitely, they were not good at identifying customer needs.
 
The others were well-prepared. They asked the right questions and expressed the interest to my business. Some candidates even learned our competitors. One company showed the great approach to the work processes. Here is the process routine they follow with the clients:
  • 2 weekly email reports: 1 working-plan report, 1 results report;
  • Trello dashboard as a tool and how-to-use guide for customers;
  • 1 weekly Skype call;
  • key project manager for communication and the team members the client can refer to;
  • payment within the certain number of days after receiving the invoice.

This information made me more confident in that executor. So that, Skype call is one of the most important steps in building relationships with the customers. On this step, you can start discovering customer needs. I understood the following:

When we communicate by voice, we gain trust. Skype call is a good way to do that. Learn project requirements and speak in a friendly manner. Show that your understand the client’s problem. It deserves the respect.

Project Estimation

understand clients: provide clear reports

Project duration. The candidates determined the project term in months, more seldom – in weeks. Nobody provided me with the detailed estimation broken into small milestones. The most I’ve got was a description of the work scope broken into work processes. They were backend development, frontend development, web design, management, testing, and analysis. It didn’t help me to understand the final cost of project development.

Engagement models. Almost nobody wanted to use the Fixed Price model. The reason was the necessity of incorporating some extra tasks and edits along the way. The solution for that was the following. They offered to follow the Agile model with breaking the project into milestones. We had to pay for each milestone. This works well enough.

Candidates ready to work on fixed price required the approved high-fidelity prototypes. High-fidelity prototypes are interactive mockups allowing the simulation of user actions. In this case, your cooperation starts with creating such prototypes. And the company includes this works to invoice.

Business Proposal. Several candidates impressed me with their business proposal. It’s convenient to receive negotiations result packed in a beautiful business proposal. This way you can find out the main points without any efforts.

The business proposal consists of the following parts:
  • technical solution;
  • description of the project team;
  • project duration represented in graphics;
  • final cost;
  • payment terms;
  • presentation of the company, etc.

It is very useful, and finally, I can say:

While requesting the estimation, customers look for the details about the future cooperation. duration, engagement model, and the final cost. Create a complete business proposal. It may include negotiations results, technical solution, and information about your company. Highlighting the main points shows your care and respect.

See What Matters

understand clients: see what matters

I became a client for a while. I had a chance to make sure of a great importance of knowing your customer. I checked cooperation systems implemented at WEB4PRO and the other companies first-hand. Now I know what the clients want.

Qualitative portfolio. The more complicated, beautiful, and modern works you show, the better.

Video Skype call. There is more trust between people who can look at each other. If we take WEB4PRO, here we always talk via Skype using video if the client turns on the camera.

Understanding the problem. It’s nice to get some questions about your business, goals, target audience, differences from competitors. That shows you know how to meet customer expectations.

Lean business processes. It’s very useful when the company knows how to work with clients. It’s great when their managers are always ready to help you out. That makes you feel convinced.

Open hourly rates. Good when executors don’t hide their hourly rates. A clear estimation should include the cost for project managers, analytics, QAs works.

Detailed Estimation. It’s better to express the project duration in hours. Also, breaking the process into milestones makes the difference. That helps the client understand the final costs.

Friendly communication. When you feel convenient speaking with a person, you’ll like to talk again. You event don’t pay too much attention to high rates and prices if the person is friendly.

Adherence to deadlines. If the executor sticks to the schedule at the beginning, they might adhere to the deadlines along the way.

Long-term partnership. I noted the company which described their partnership with Australian clients. Their projects were large enough. Who knows, but they tried to support their achievements. But all in all, they impressed me.

Results of negotiations. It’s pretty useful to receive an email with the results of negotiations after the meeting. This type of report is a must-do point at our company. So, now I’m confident in its efficiency and necessity.

Business proposal. It helps to sum up the main ideas and points of cooperation. A good marketing kit provides the full picture of the business processes you are going to follow.

What you can do to be on the same wavelength with a customer:

  • learn client’s project requirements;
  • wonder about the client’s values and business ideas;
  • don’t ask the needless questions: try to find the answers in the project specification;
  • don’t hide your hourly rate. Be honest;
  • prepare detailed project estimation;
  • and the main: keep your word.

Do we have anything to improve? Of course! Anyone who works with customers should always work on understanding clients needs. But it’s pleasant to note that we are on the right way at WEB4PRO. Our work processes are well-thought-out. They help the whole team work well and cooperate with customers with the high efficiency.

What do the clients want? Well, the clients want to understand what happens and what they pay for. They want to be confident in the executor, and they want to secure themselves from risks. Finally, a client is a person, who wants to deal with friendly, honest, and professional people.

How to understand clients? – Step into their shoes at least once. Then a lot of steps and routine work points will matter much more to you. These are my words, the words of Project Manager:)

P.S. This rule works with any person, not only with your clients. As Harper Lee says in the book To Kill A Mockingbird, “You never understand a person… until you climb into his skin and walk around in it.”

The featured image: the scene from “What women want”. The main character climbed into women skin in the truest sense of the word.

Migration from Magento 1 to Magento 2 using Magento 2 Migration Tool

Welcome! Today we’ll show you how to migrate Magento® 1 to Magento 2 using Magento 2 data migration tool. You’ll also learn how you can manage themes, extensions, and customization. Together we’ll follow the next script:

Let’s get the ball rolling!

Magento 2 Usage Statistics

Magento 1 stops official updates in November 2018. According to BuiltWith, the number of live websites using Magento 2 grew up to 13500 at the moment (July 2017). There were 11000 live websites in May 2017. As you can see, Magento 2 becomes more and more popular, while the first version still exists. If you’ve made a decision to move to Magento 2, there is a complete guide on how to do this. But this task requires strong web development skills, some effort and a bit of patience:)

Magento 1 to Magento 2 Migration Process

While migrating your website from Magento 1 to Magento 2, you’ll work with four components:

  • data;
  • themes,
  • extensions and custom code;
  • customization.

Each of these components requires a certain approach.

Our today’s task is to migrate data with Magento 2 Data Migration Tool. Follow our step-by-step guide below, and you’ll make it!

Migrating Data with Magento 2 Data Migration Tool

Before we start, we should make several preparation steps. Let’s run through them.

Step 1: Software and Hardware Requirements

Preconditions

  • Check system requirements. Check parameter in php.ini

memory_limit: more than 512M

  • Install Data Migration Tool. Versions of Magento and Data Migration Tool must match.

How to Install Data Migration Tool

First, you need to edit composer.json in the Magento root directory. This way you provide the location of the Data Migration Tool package: “URL”: “https://repo.magento.com/.” Also add to require “magento/data-migration-tool”: “version”, where “version” must match the Magento 2 code base version.

  • Create a Magento 2 database backup. This will allow you to restore the initial database state if migration is not successful.
  • Check the network access to connect Magento 1 and Magento 2 databases.
  • Copy Magento 1.x media files to Magento 2.x. You need to copy them manually from magento1-root/media to magento2-root/pub/media directory.
  • Stop all Magento 1.x cron operations.
  • Stop making any changes in Magento 1.x Admin except for order management and in Magento 2 Admin and storefront.
  • Remove outdated and redundant data from Magento 1.x database (logs, order quotes, recently viewed or compared products, visitors, event-specific categories, promotional rules, etc.).

Step 2: Theme Migration

As for themes, we’ll need to make changes to themes and customizations for Magento 2, because the system’s hierarchy is completely different from Magento 1. So, you’ll find lots of space for your creativity and innovation.

Step 3: Extension Migration

We can go to Magento Marketplace or MagentoConnect and get new Magento 2 extensions with their latest versions. Also, we can develop a new custom solution. If you want to migrate the custom code, you can use Code Migration Toolkit presented by Magento on Github.

According to the Magento community, migration to Magento 2 is 20% more complicated than updating Magento to the latest minor version. Opinions differ. It depends on the level of the developer’s experience and the project complexity.

Step 4: Data Migration

Magento 2 Data Migration Tool is a ready-made assistant for migrating the website data. We can move all our customers, products, store configurations, order and promotions data to Magento 2 with its help. It is the process we’ll focus on in our article.

Configuring Migration to Magento 2

If we need to migrate Magento 1.x EE to Magento 2.x EE, the mapping and configuration files will be located in the following directory:

<your Magento 2 install dir>/vendor/magento/data-migration-tool/etc/ee-to-ee

Create a config.xml from the provided sample config.xml .dist.

If we migrate data from Magento 1.14.1.0, the config.xml and map.xml. will be located in the following directory:

<your Magento 2 install dir>/vendor/magento/data-migration-tool/etc/ee-to-ee/1.14.1.0

But before, enable to perform custom database mapping between your Magento 1 and Magento 2 databases.

Specify the access to the databases in config.xml:

<source>
    <database host="127.0.0.1" name="magento1" user="root"/>
</source>
<destination>
    <database host="127.0.0.1" name="magento2" user="root"/>
</destination>
<options>
    <crypt_key />
</options>

The <crypt_key> tag must be filled in. You can find it in the local.xml file. The file is located in Magento 1 instance directory at app/etc/local.xml in the <key> tag.

Migration to Magento 2 Modes

Overall, migration from Magento 1 to Magento 2 consists of three phases (modes):

  • Settings: migrates configuration settings.
  • Data: bulk migrates the main data to the database.
  • Delta: transfers incremental data updates added to Magento 1 storefront and Admin Panel while running previous migration modes.

Each mode is declared in config.xml and divided into steps. Each step is responsible for transferring particular data. At the begin of the run, the step checks Magento 1 and Magento 2 table structures for consistency. Then the actual data is transferred to Magento 2. In the end, this data is verified.

If you don’t want to migrate some data from Magento 1 to Magento 2, you just need to disable or remove the specific step in config.xml. For example, remove the following step from Data mode:

<step title="Log Step">
   <integrity>Migration\Step\Log\Integrity</integrity>
   <data>Migration\Step\Log\Data</data>
   <volume>Migration\Step\Log\Volume</volume>
</step>

And remove this one from delta mode:

<step title="Log Step">
   <delta>Migration\Step\Log\Delta</delta>
   <volume>Migration\Step\Log\Volume</volume>
</step>

Furthermore, if we do not want to track the changes in the log_visitor table, we need to remove “delta_log” group in deltalog.xml.dist file:

<group name="delta_log">
   <document key="visitor_id">log_visitor</document>
</group>

Migrating Settings and Data to Magento 2

So, we successfully moved to the finish.

To start migrating settings, run:

bin/magento migrate:settings [-r|--reset][-vvv] {<path to config.xml>}

where:

  • [-r|–reset] is an optional argument that starts the migration from the beginning. You can use this argument for testing migration.
  • {<path to config.xml>} is the absolute file system path to config.xml.
  • [ -vvv] – you can use this argument to output more verbose messages in the console.
<your Magento 2 install dir>/vendor/magento/data-migration-tool/etc/ee-to-ee/1.14.1.0/config.xml

To start migrating data, run:

bin/magento migrate:data [-r|--reset] {<path to config.xml>}

To start migrating incremental changes, run:

bin/magento migrate:delta [-r|--reset] {<path to config.xml>}

Incremental migration enables to migrate only data that customers added via storefront (created orders, reviews, changes in customer profiles, etc.) and all operations with orders in Magento Admin panel.

Incremental migration runs continuously until you stop it by pressing CTRL C.

Reindex all Magento 2.x indexers after migration.

Possible Problems

We can get the following error during Data Integrity Step:

  • Error: Foreign key (<KEY_NAME>) constraint fails. Orphan records id:<id> from <child_table>.<field_id> has no referenced records in <parent_table>

Solution: There are missing database records in the parent_table. The field_id of the child_table is pointing to this parent_table.

If we can not delete the records from the child_table, we can just disable the Data Integrity Step in config.xml:

<step title="Data Integrity Step">
<integrity>Migration\Step\DataIntegrity\Integrity</integrity>
</step>

The data migration tool recognizes the differences in database structure between Magento 1.x versions. Most of these database structural differences are declared in map files. Each step in the process uses map files to transform data for use in the Magento 2 store. When differences are not declared in map files, then the Data Migration Tool displays an error and does not run.

If some Magento 1 entities (in most cases, coming from extensions) do not exist in Magento 2 database, the following errors may occur:

  • Error: Source documents are not mapped: <EXTENSION_TABLE>

Solution: Install the corresponding Magento 2 extensions or ignore the problematic data in map.xml (or map.xml.dist).

<source> 
  <document_rules> 
      <ignore>
          <document><EXTENSION_TABLE</document>
      </ignore> 
  </document_rules> 
</source>
  • Error: Source fields are not mapped. Document: <EXTENSION_TABLE>. Fields: <EXTENSION_FIELD>

Solution:

<source> 
  <document_rules> 
      <ignore>
          <document><EXTENSION_TABLE>.<EXTENSION_FIELD</document>
      </ignore>
  </document_rules> 
       </source>
  • Error: Destination documents are not mapped: <EXTENSION_TABLE>

Solution:

<destination>
<field_rules>
<ignore>
     <field><EXTENSION_TABLE>.<EXTENSION_FIELD></field>
</ignore>
</field_rules>
 </destination>
  • Error: Mysql server has gone away

Solution: Increase the following value in the /etc/mysql/my.cnfmysql configuration file:

  • max_allowed_packet = 2000M
  • wait_timeout = 6000

3 Best Magento SEO Extensions in 2019

We continue to work on Magento® SEO. Here you’ll find Top 3 Best External Magento SEO extensions for performing the best search engine optimization practices. We have already learned the things that matter in Magento SEO and how to set them up.  Now there is an additional help for making your Magento website SEO-friendly.

Best External Magento SEO Extensions: Our Choice

All steps to SEO become much easier and convenient with external Magento SEO extensions. We‘ve chosen three of them which are the best in our humble opinion. Here they are:

SEO Toolkit by Amasty

This extension includes the numerous features of Magento SEO. It will help you to do the following:

  • work with categories and products;
  • create relevant metadata for all pages;
  • export and import product tags;
  • generate the sitemaps for a multiple Magento online store;
  • create SEO-friendly short URLs;
  • make SEO-friendly product reviews.

It’s a really good solution, and it comes with a user-friendly guide on how to make all thing works.

Where to find the extension and guide: amasty.com

There you’ll also find the detailed description of a product.

Magento SEO Suite Ultimate extension v9.0.1 by Mageworx

This is the all-in-one Magento SEO solution. It can do the same things as the previous one. So you can choose the extension you like more. Let’s take a look at what it can:

  • has 20 templates for work with metadata;
  • helps to create advanced Magento snippets;
  • performs SEO for category and product pages;
  • includes features for SEO breadcrumbs and redirects;
  • allows external and internal cross-linking;
  • generates extended XML and HTML sitemap;
  • provides the detailed SEO reports.

It is also packaged with users guide that will help you to achieve the best results in working with Magento SEO features.

Where to find the extension and guide: mageworx.com

Create HTML Sitemap for SEO by Creare – Free

The extension is focused on creating an HTML sitemap for Magento store, and it’s available for free on MagentoConnect. Here is how it works::

  • lists all Magento store categories in cascading style;
  • creates the SEO-friendly sitemap;
  • replaces the defaulted paginated sitemap of Magento;
  • lists all static and CMS pages;
  • helps to make all category pages indexed;
  • has built-in XML checker.

Where to find the extension: magentocommerce.com