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

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

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

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

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

Magento 2 grid Creation with custom query

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

namespace Namespace\ModuleName\Model\ResourceModel\SpCommission;

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

class Collection extends AbstractCollection {

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

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

    }
}

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

Field is ambiguous

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

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

total_commission(Aggregate function field) is not a column

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

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

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

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

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

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

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

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

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

Magento 2 Request Flow In Depth

So far we have seen about the routers of magento but let’s understand the request flow by creating a simple controller, you can consider any existing controller and traverse through following steps by applying echo or var_dump. Lets start with our magento entry file

Index.php #39 
Code:-
/** @var \Magento\Framework\App\Http $app */
$app = $bootstrap->createApplication('Magento\Framework\App\Http');
$bootstrap->run($app);

Let us enter to our run function which is taking $app as parameter ($app is an instance of \Magento\Framework\App\Http). In bootstrap.php we have run function which launches the application.

class: \Magento\Framework\App\Http
public function run(AppInterface $application)
    {
        try {
            try {
                \Magento\Framework\Profiler::start('magento');
                $this->initErrorHandler();
                $this->initObjectManager();
                $this->assertMaintenance();
                $this->assertInstalled();
                $response = $application->launch();
                $response->sendResponse();
                \Magento\Framework\Profiler::stop('magento');
            } catch (\Exception $e) {
                \Magento\Framework\Profiler::stop('magento');
                if (!$application->catchException($this, $e)) {
                    throw $e;
                }
            }
        } catch (\Exception $e) {
            $this->terminate($e);
        }
    }

It time to go now to launch function in Magento\Framework\App\Http and check the code is calling dispatch method of frontcontroller, which is then calling match function of routers and as per our previous post we know our request will go to base router.

class: Magento\Framework\App\Http
$result = $frontController->dispatch($this->_request);

So we will be looking into Magento\Framework\App\Router\Base.php for match function, while looking closely to match function we can see it calls to matchAction and return its output.

class: Magento\Framework\App\Router\Base
public function match(\Magento\Framework\App\RequestInterface $request)
    {
        $params = $this->parseRequest($request);

        return $this->matchAction($request, $params);
    }

And here is the code of matchAction that we need to discuss:-

$actionInstance = $this->actionFactory->create($actionClassName);

$this->ActionFactory is an instance of Magento\Framework\App\ActionFactory and if we will look in create function, we have call to object manager which we know is responsible for creating instances.

class: Magento\Framework\App\ActionFactory
    public function create($actionName)
    {
…
        return $this->_objectManager->create($actionName);
    }
Now let’s see how objectmanager create an instance of every parameter in contructor

i.e, dependency injection in constructor.

Above $this->_objectManager is an instance of Magento\Framework\ObjectManager\ObjectManager. Here you can see the create function which is calling another create function:-

class: Magento\Framework\ObjectManager\ObjectManager
public function create($type, array $arguments = [])
    {
        $type = ltrim($type, '\\');
        //var_dump(get_class($this->_factory)); 
        return $this->_factory->create($this->_config->getPreference($type), $arguments);
    }


$this->_factory
is an object of Magento\Framework\ObjectManager\Factory\Dynamic\Developer class. Basically we have two factory class of object manager one is for production mode and another for development. If we were in producttion mode then $this->_factory will be an instance of Magento\Framework\ObjectManager\Factory\Dynamic\Production

In Developer .php we have create function which is getting the constructor parameter using line

$parameters = $this->definitions->getParameters($type);

$this->definition is an instance of Magento\Framework\ObjectManager\Definition\Runtime
And here in getParameters function we can see that parameters of constructor are being fetched one by one.

class: Magento\Framework\ObjectManager\Definition\Runtime
public function getParameters($className)
    {
        if (!array_key_exists($className, $this->_definitions)) {
            $this->_definitions[$className] = $this->_reader->getConstructor($className);
        }
        return $this->_definitions[$className];
    }

Going back to create function of Magento\Framework\ObjectManager\Factory\Dynamic\Developer, we have return statement as return $this->createObject($type, $args);. Here
$type = classname
$args = list of partameters

Now $this->createObject is an instance of Magento\Framework\ObjectManager\Factory\AbstractFactory

And here you can clearly see that objects are created in below function

class: Magento\Framework\ObjectManager\Factory\AbstractFactory
createObject($type, $args)
    {
        return new $type(...array_values($args));
    }

Magento 2 Routing

Routing is the most important part of any application or framework. This blogpost covers the request flow of magento or routing process in magento2. So lets start with our index.php which is an entry file for magento. So you can see the following code:-

$bootstrap->run($app);

Now moving on to run function of \Magento\Framework\App\Bootstrap see the below code:-

$response = $application->launch();
$response->sendResponse();

Here it calls the launch function of AppInterface which is impelmeented by Magento\Framework\App\Http, now lets look into the launch() function.

public function launch()
    {
        $areaCode = $this->_areaList->getCodeByFrontName($this->_request->getFrontName());
        $this->_state->setAreaCode($areaCode);
        $this->_objectManager->configure($this->_configLoader->load($areaCode));
        /** @var \Magento\Framework\App\FrontControllerInterface $frontController */
        $frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');
        $result = $frontController->dispatch($this->_request);
        // TODO: Temporary solution until all controllers return ResultInterface (MAGETWO-28359)
        if ($result instanceof ResultInterface) {
            $this->registry->register('use_page_cache_plugin', true, true);
            $result->renderResult($this->_response);
        } elseif ($result instanceof HttpInterface) {
            $this->_response = $result;
        } else {
            throw new \InvalidArgumentException('Invalid return type');
        }
        // This event gives possibility to launch something before sending output (allow cookie setting)
        $eventParams = ['request' => $this->_request, 'response' => $this->_response];
        $this->_eventManager->dispatch('controller_front_send_response_before', $eventParams);
        return $this->_response;
    }

Our function is first fetching the area code i.e, frontend or admin, then it sets the area code and configure it. Then we are calling dispatch function of Magento\Framework\App\FrontControllerInterface

$frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');
$result = $frontController->dispatch($this->_request);

Now lets look at dispatch method, similar to Magento1 it loops through all router.

/Magento/Framework/App/FrontController.php
class FrontController implements FrontControllerInterface
{
    public function dispatch(RequestInterface $request)
    {
        \Magento\Framework\Profiler::start('routers_match');
        $routingCycleCounter = 0;
        $result = null;
        while (!$request->isDispatched() && $routingCycleCounter++ < 100) {
            /** @var \Magento\Framework\App\RouterInterface $router */
            foreach ($this->_routerList as $router) {
                try {
                    $actionInstance = $router->match($request);
                    if ($actionInstance) {
                        $request->setDispatched(true);
                        $actionInstance->getResponse()->setNoCacheHeaders();
                        $result = $actionInstance->dispatch($request);
                        break;
                    }
                } catch (\Magento\Framework\Exception\NotFoundException $e) {
                    $request->initForward();
                    $request->setActionName('noroute');
                    $request->setDispatched(false);
                    break;
                }
            }
        }
        \Magento\Framework\Profiler::stop('routers_match');
        if ($routingCycleCounter > 100) {
            throw new \LogicException('Front controller reached 100 router match iterations');
        }
        return $result;
    }
}

$this->_routerList is coming from Magento\Framework\App\RouterList, if you will echo in getRouterInstance(), then you will get the list of all routers. (Note, try for page that does not exist so that all router name can be listed).
Below is the sequence of router and their class name respectively.

Standard - Magento\Framework\App\Router\Base
Urlrewrite - Magento\UrlRewrite\Controller\Router
Cms - Magento\Cms\Controller\Router
Default - Magento\Framework\App\Router\DefaultRouter

We will discuss these router in later part of post lets complete dispatch function first. Dispatch method loops through all router until any match is found or routingcyclecounter exceeds 100. Once any match is found request will be dispatched through that router and response will be shown accordingly.

Lets take a brief look on our routers responsibility now:-

Base or standard Router

This is located at Magento\Framework\App\Router\Base and is the first router in loop. Match() method will parse the request and will match the action using url(front name/action path/action/param 1/etc params/).

Url Rewrite Router

This is located in app/code/Magento/UrlRewrite/Controller/Router.php, if you look at match function then you can see the urlfinder which are fetching the entity-type and id from url key to generate required routing setting i.e, controller, action, and module. Once match is found these will be forwarded back to standard router for match.

Cms Router

It handles the cms request, it basically fetch the page id by using url-key and then set module name as cms, controller name as page and action as view i.e, app/code/Magento/Cms/Controller/Page/View.php controller. After setting these details rather than dispatching the request it break current loop and and start loop again to match it with base router.

Default Router

It is used when match is not found in any other router, used for 404 page error type request.

Magento 2:- Add a Grouped product programmatically

Follow my previous blogpost to create a simple product with magento that will give you a basic idea of creating products. In this blogpost we will continue from that post only. So to create product as grouped, we simply link some products to it.
In previous post code you also need to change type ID as grouped. Grouped product are added to link as related product are linked, below is the code that you need to add before saving product.

$childrenIds = array(53,54,55);
        $associated = array();
        $position = 0;
        foreach($childrenIds as $productId){
            $position++;
            //You need to load each product to get what you need in order to build $productLink
            $linkedProduct = $productRepository->getById($productId);
            
            /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */
            $productLink = $objectManager->create(\Magento\Catalog\Api\Data\ProductLinkInterface::class);

            $productLink->setSku('sku')
                ->setLinkType('associated')
                ->setLinkedProductSku($linkedProduct->getSku())
                ->setLinkedProductType($linkedProduct->getTypeId())
                ->setPosition($position)
                ->getExtensionAttributes()
                ->setQty(0);

            $associated[] = $productLink;
        }
        $product->setProductLinks($associated);

In below image you can see the data added to our product:-

Magento 2:- Add a bundled product programmatically

Follow my previous blogpost to create a simple product with magento that will give you a basic idea of creating products. In this blogpost we will continue from that post only. So to create product as bundled, we need to set two more attribute value i.e, price_view and price_type.
In previous post code you also need to change typeId as bundles. After creating a product with type bundled we have to add a link and option information for the product.
Firstly lets see the modified attribute:-

 $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE); 
 $product->setPriceType(1); 
 $product->setPriceView(1);

To add a link use the below code.

$link = $objectManager->create(\Magento\Bundle\Api\Data\LinkInterface::class);
$link->setPosition(0);
$link->setSku('MH01-XS-Orange');
$link->setIsDefault(false);
$link->getQty(1);
$link->setPrice(12);
$link->setPriceType(\Magento\Bundle\Api\Data\LinkInterface::PRICE_TYPE_FIXED);
 

To add link to option add below code:-

$optionRepository = $objectManager->create(\Magento\Bundle\Api\ProductOptionRepositoryInterface::class);
$option = $objectManager->create(\Magento\Bundle\Api\Data\OptionInterface::class);
$option->setTitle('Size');
$option->setType('radio');
$option->setRequired(true);
$option->setPosition(1);
$option->setProductLinks(array($link));
$optionRepository->save($product, $option);

In below image you can see the data added to our product:-

Magento 2:- Create a downloadable product programmatically

Follow my previous blogpost to create a simple product with magento that will give you a basic idea of creating products. In this blogpost we will continue from that post only. So to create product as downloadable, we need to set weight as NO and add details under downloadable Information tab.
In previous post code just set typeId as downloadable and product has weight to NO. After creating a product with type downloadable we have to add a link and sample data information for the product. To add a link use the below code.

$link_repository = $objectManager->create('Magento\Downloadable\Api\LinkRepositoryInterface');
$link_interface = $objectManager->create('\Magento\Downloadable\Api\Data\LinkInterface');
$link_interface->setTitle('first downloable product');
$link_interface->setPrice(9);
$link_interface->setNumberOFDownloads(10);
$link_interface->setIsShareable(1);
$link_interface->setLinkType('url');
$link_interface->setLinkUrl('http://www.w3solver.com');
$link_interface->setSampleType('url');
$link_interface->setSampleUrl('http://w3solver.com');
$link_interface->setIsUnlimited(0);
$link_interface->setSortOrder(0);
$link_repository->save('sku', $link_interface); // param1 is the sku of your product

To add data under sample section:-

$sample_repository = $objectManager->create('Magento\Downloadable\Api\SampleRepositoryInterface');
$sample_interface = $objectManager->create('\Magento\Downloadable\Api\Data\SampleInterface');
$sample_interface->setTitle('first downloable product');
$sample_interface->setSampleType('url');
$sample_interface->setSampleUrl('http://www.w3solver.com/download/qrcode.zip');
$sample_interface->setSortOrder(0);
$sample_repository->save('sku', $sample_interface);

In below you can see the data added to our product:-

Magento 2 Complete Guide to add a product programmatically

There are several scenario when we might need to add our product programmatically in Magento. This blog-post will provide you a detail guide to add the product in database programmatically.
You can follow below posts for creating different product of various type:-

Adding a simple product

Below code will add a new product to magento 2 with SKU as “W3PRO1”, comments are properly given to make the code self-explainable. We have added product with ‘default’ attribute set.

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); // instance of object manager
        $product = $objectManager->create('\Magento\Catalog\Model\Product');
        $product->setSku('W3PRO1'); // Set your sku here
        $product->setName('Sample Product'); // Name of Product
        $product->setAttributeSetId(4); // Attribute set id
        $product->setStatus(1); // Status on product enabled/ disabled 1/0
        $product->setProductHasWeight(1); // if product has weight
        $product->setWeight(10); // weight of product
        $product->setVisibility(4); // visibilty of product (catalog / search / catalog, search / Not visible individually)
        $product->setTaxClassId(0); // Tax class id
        $product->setTypeId('simple'); // Type of product (simple/virtual/downloadable/configurable)
        $product->setPrice(100);  // price of product
        $product->setStockData(
                     array(
                        'use_config_manage_stock' => 0,
                        'manage_stock' => 1,
                        'is_in_stock' => 1,
                        'qty' => 99
                         )
                     );
        $product->setNewsFromDate('11-11-2017'); // set the data from which product will be marked as new
        $product->setNewsToDate('11-12-2017'); // set the data till when product will be marked as new
        $product->setCountryOfManufacture('AL'); // Set country of manufacture

        $product->save();
Note: It is not recommended to create object using object manager, you should inject dependency through constructor and then further use them for object creation 

Add Related, Upsell and Crosssell to product

Above code is the basic example for adding product, now if we want to add related, upsell or crosssell product then we need to add below code before saving the products. Below we have just given example for related. Crosssell and upsell will also be created in the same way.

$this->_productLink = $objectManager->create('\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory ');
$skuLinks = '24-MB01,24-MB04,24-MB03';
 $skuLinks = explode(",",$skuLinks);
foreach($skuLinks as $skuLink) {
    $linkData = $this->_productLink->create();
    $linkData->setSku($product->getSku())
             ->setLinkedProductSku($skuLink)
             ->setLinkType("related");
    $linkDataAll[] = $linkData;
}
if($linkDataAll) {
   /* this will set related products detail, similarly we can add upsell and crossell */
   $product->setProductLinks($linkDataAll); 
}

Add Customizable Options

$this->_productCustomOption = $objectManager->create('\Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory');

$customOption = $this->_productCustomOption->create()->setTitle('Text')
                     ->setType('area')
                     ->setIsRequire(true)
                     ->setSortOrder(1)
                     ->setPrice(1.00)
                     ->setPriceType('fixed')
                     ->setMaxCharacters(50)
                     ->setProductSku($product->getSku());

$customOptions[] = $customOption;
$product->setOptions($customOptions);

Add website design, Schedule design update, gift options

$product->setWebsiteIds(array(1));  //add website id

/* Add design Attribute*/
$product->setPageLayout('1column');  
$product->setOptionsContainer('container1');
$product->setCustomLayoutUpdate('');

/* Add Schedule Design Update*/
$product->setCustomDesignFrom('1-6-2017');
$product->setCustomDesignTo('1-6-2017');
$product->setCustomDesign(1);
$product->setCustomLayout('1column');

/* Add Gift Options*/
 $product->setGiftMessageAvailable(1);

Magento 2 QRCode Generator extension

QR code (Quick Response Code) is the matrix barcode which is a machine-readable optical label that contains information about the item in our case it will have url of the products.

General Features of the extension

• One of the good marketing tool to drive sales and provides a modern image to your brand
• Enticing the mobile consumers by letting them simply scan the QR code and shop even on the go
• Incorporate QR code poster ads, in-store display, print ads etc

Feature List

– QR Codes for every Product Detail Page
– QR Codes list page in admin to generate mass Qrcode and print option
– No other plugins needed
– Qrcode can be generated for individual product edit page

Qrcode User Guide

1.) Once installed you can review the Qrcode menu under (product > Qrcode > List Qrcode) and view all product list with Qrcode details. List page is created to provide an option to mass generate Qrcode or print them.

2.) You can also generate Qrcode from product edit page. There is a tab of Qrcode and button provided separately for each product.

3.) On frontend product detail page, Qrcode is also available under Qrcode tab similar to review tab.

Versions: I’ve tested the module with the version 2.1.5

click link to download the extension

Let me know if you found any issue or have any query.

Magento 2:- Debugging and Customization of Mini Cart

I found lots of developer struggling out with customization of minicart in Magento 2, so thought to write this. Magento2 has modified things lots to render minicart using knockout js, template and layout xml. We will be going through debugging process step by step to understand more, drop in comments if you have any issue with any part.

Below you can see the image for minicart in magento 2:-

If you inspect the html code you will land up with something like this:-

From where does that “minicart-wrapper” comes, here we found it

vendor\magento\module-checkout\view\frontend\templates\cart.phtml

Let’s review layout file which is responsible to call this template:-

vendor\magento\module-checkout\view\frontend\layout\default.xml

<referenceContainer name="header-wrapper">
    <block class="Magento\Checkout\Block\Cart\Sidebar" name="minicart" as="minicart" after="logo" template="cart/minicart.phtml">
        <arguments>
            <argument name="jsLayout" xsi:type="array">
                <item name="types" xsi:type="array"/>
                    <item name="components" xsi:type="array">
                        <item name="minicart_content" xsi:type="array">
                            <item name="component" xsi:type="string">Magento_Checkout/js/view/minicart</item>
                            <item name="config" xsi:type="array">
                                <item name="template" xsi:type="string">Magento_Checkout/minicart/content</item>
                            </item>
                            <item name="children" xsi:type="array">
                                <item name="subtotal.container" xsi:type="array">
                                    <item name="component" xsi:type="string">uiComponent</item>
                                    <item name="config" xsi:type="array">
                                        <item name="displayArea" xsi:type="string">subtotalContainer</item>
                                    </item>
                                    <item name="children" xsi:type="array">
                                        <item name="subtotal" xsi:type="array">
                                            <item name="component" xsi:type="string">uiComponent</item>
                                            <item name="config" xsi:type="array">
                                               <item name="template" xsi:type="string">Magento_Checkout/minicart/subtotal</item>
                                            </item>
                                        </item>
                                   </item>
                                </item>
                                <item name="extra_info" xsi:type="array">
                                    <item name="component" xsi:type="string">uiComponent</item>
                                        <item name="config" xsi:type="array">
                                            <item name="displayArea" xsi:type="string">extraInfo</item>
                                        </item>
                                    </item>
                                    <item name="promotion" xsi:type="array">
                                        <item name="component" xsi:type="string">uiComponent</item>
                                        <item name="config" xsi:type="array">
                                            <item name="displayArea" xsi:type="string">promotion</item>
                                    </item>
                               </item>
                        </item>
                    </item>
                </item>
            </argument>
        </arguments>
        <container name="minicart.addons" label="Mini-cart promotion block"/>
    </block>
</referenceContainer>

We will be going through this layout xml step by step, lets understand below line


<block class="Magento\Checkout\Block\Cart\Sidebar" name="minicart" as="minicart" after="logo" template="cart/minicart.phtml">

So above line add a block with block class as Magento\Checkout\Block\Cart\Sidebar.php and template file path as

 Magento-Checkout/view/frontend/templates/cart/minicart.phtml

Let’s look at our template file minicart.phtml code to find “minicart-content-wrapper”class


<div id="minicart-content-wrapper" data-bind="scope: 'minicart_content'">
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

So we got our class, now check the statement getTemplate using knockout JS. From where does our template is now coming????? Oh, its time to go back and review default.xml once again and get the block components details(check for below code).


<item name="config" xsi:type="array">
    <item name="template" xsi:type="string">Magento_Checkout/minicart/content</item>
</item>

Above code provides the template details:-

Magento_Checkout/View/frontend/web/template/minicart/content.html

You can find your sidebar template here. We will be going in depth with items coming in sidebar (rest code will become quite easy for you to understand once we will cover items part). Check below code for this:-

<!-- ko if: getCartParam('summary_count') -->
    <strong class="subtitle"><!-- ko i18n: 'Recently added item(s)' --><!-- /ko --></strong>
    <div data-action="scroll" class="minicart-items-wrapper">
        <ol id="mini-cart" class="minicart-items" data-bind="foreach: { data: getCartParam('items'), as: 'item' }">
            <!-- ko foreach: $parent.getRegion($parent.getItemRenderer(item.product_type)) -->
                <!-- ko template: {name: getTemplate(), data: item, afterRender: function() {$parents[1].initSidebar()}} --><!-- /ko -->
            <!-- /ko -->
        </ol>
    </div>
    <!-- /ko -->

Above code has ordered list to show item recursively and data is being fetched using foreach loop of knockout.js

foreach: { data: getCartParam('items'), as: 'item' }

From where can I get this data in foreach, this question might be coming to your mind…?.. Lets discover it; where can it be? The answer can be found in default.xml, check below line of code:-

Magento_Checkout/js/view/minicart

So here we got the path of JS from where we can check about data..
Path to js file:-

Magento_Checkout/view/frontend/web/js/view/minicart.js

As you can see we have a function getCartParam(‘items’) in foreach, this function can be found in line 150,

	getCartParam: function (name) {
            if (!_.isUndefined(name)) {
                if (!this.cart.hasOwnProperty(name)) {
                    this.cart[name] = ko.observable();
                }
            }

            return this.cart[name]();
        }

This function gets the value from cart variable, which is getting its value set in update function

update: function (updatedCart) {
            _.each(updatedCart, function (value, key) {
                if (!this.cart.hasOwnProperty(key)) {
                    this.cart[key] = ko.observable();
                }
                this.cart[key](value);
            }, this);
        },

Update function is called in initialize(), and passing cartData() as a parameter, cartData is getting its value from customerData.get(‘cart’) i.e, we now need to check for customerData object get() function.

initialize: function () {
            var self = this,
                cartData = customerData.get('cart');
            this.update(cartData());
……..

CustomerData is being set using ‘Magento_Customer/js/customer-data’. Before we proceed a quick idea about define function. This function is from requireJs – first parameter of “define” function is array of dependency and second parameter is definition of our function. Definition function should always return an object. So now lets look at

Magento_Customer/view/frontend/web/js/customer-data.js

This file is responsible for sending ajax request to get data. Lets traverse through its code one by one. As mentioned above we have to reach customerData.get() , check below code in this file for the same:-

get: function (sectionName) {
            return buffer.get(sectionName);
        },

Now as we can see it is calling buffer.get(), so lets move to that code:-

get: function (sectionName) { 
            if (!this.data[sectionName]) {
                this.bind(sectionName);
            }

            return this.data[sectionName];
        },

Here code is calling bind() function which is setting value in data model:-

bind: function (sectionName) {
            this.data[sectionName] = ko.observable({});
        },

Now if you check customer.init() function there is a check for needReload(), which tells if we need reload or data should come from cache directly. Here we are either calling this.reload() if reload is needed or we are getting data from cache dataProvider.getFromStorage(), no need to reload here if keys validated properly.

Let check the reload function which is calling dataProvider.getFromServer() function and that is sending ajax request to get customer data:-

reload: function (sectionNames, updateSectionId) {
            return dataProvider.getFromServer(sectionNames, updateSectionId).done(function (sections) {
                buffer.update(sections);
            });
        },

Now check the getfromServer(), here you can see an ajax call.

            return $.getJSON(options.sectionLoadUrl, parameters).fail(function (jqXHR) {
                throw new Error(jqXHR);
            });

You must be thinking we have jumped from this.data[sectionName] = ko.observable({}); directly to customerData.init(). As we can see we are observing the data value here, so we understand the code which is responsible for providing changed value to this variable. There is one more code responsible for fetching data i.e, calling reload function

$(document).on('ajaxComplete', function (event, xhr, settings) {
        var sections,
            redirects;

        if (settings.type.match(/post|put/i)) {
            sections = sectionConfig.getAffectedSections(settings.url);

            if (sections) {
                customerData.invalidate(sections);
                redirects = ['redirect', 'backUrl'];

                if (_.isObject(xhr.responseJSON) && !_.isEmpty(_.pick(xhr.responseJSON, redirects))) {
                    return;
                }
                customerData.reload(sections, true);
            }
        }
    });

Enough of this, now lets get back to our content.html, so far we understand from where data is coming in foreach, now look at the inner foreach:-

foreach: $parent.getRegion($parent.getItemRenderer(item.product_type))

item.product_type => coming from loop
$parent.getItemRenderer => it means getItemRenderer() function of parent js file i.e, minicart.js

If you do console.log(($parent.getItemRenderer(item.product_type)), then you will get the name of displayArea to be shown. Like:- defaultRenderer

Now look into checkout_cart_sidebar_item_renderers.xml, here we have code of our block ‘minicart’, we have our region as seen in below code:-


<item name="itemRenderer" xsi:type="array">
    <item name="default" xsi:type="string">defaultRenderer</item>
    <item name="simple" xsi:type="string">defaultRenderer</item>
    <item name="virtual" xsi:type="string">defaultRenderer</item>
</item>

Just check below line to get the template of items of minicart.


<item name="displayArea" xsi:type="string">defaultRenderer</item>
<item name="template" xsi:type="string">Magento_Checkout/minicart/item/default</item>

So, we got our template file Magento_Checkout/view/frontend/web/template/minicart/item/default.html

I think that is enough for now and will help you a lot to debug and understand the code flow.