Reference : alanstorm.com/magento_2_mvvm_mvc, alanstorm.com

In Magento 2, a lot of logics are set in Modules. Modules are similar to extensions in Magento 1. We are to setup a url baseUrl/hello_mvvm/hello/world. This url will be set up automatically by setting up controllers and views.

As developing a module, we should do to things first to Configure a module to tell Magento which controller it should use for a URL. also, Configure a module to tell Magento which Block objects should be added to the system.

Module codes live in the app/code folder. It then has a two-directory structue within app/code that which first one is the vendor namespace, and next is the module name. In this example, it’s gonna be Pulsestorm, and HelloWorldMVVM. The whole module name would be vendorname and module name combined with an underscore. module.xml inside etc folder would have the structure of something like this

// app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
   <module name="Pulsestorm_HelloWorldMVVM" setup_version="0.0.1"/>
</config>

This module.xml will tell magento to add a module. Also, we will have to registration.php for Magento 2. This file will make the module to be identified to the system, similar to magento 1’s app/etc/module files. It will look something like this, although the names will be different everytime.

// app/code/Pulsestorm/HelloWorldMVVM/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Pulsestorm_HelloWorldMVVM',
    __DIR__
);

So this will surely setup our module, but it wouldn’t do any logic or show any frontend. To make the frontend, we should make a routes.xml to set the front name of the module. It would actually catch the frontname as the url itself. it would be inside etc/frontend folder.

// app/code/Pulsestorm/HelloWorldMVVM/etc/frontend/routes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <router id="standard>
        <route id="hello_mvvm" frontName="hello_mvvm">
            <module name="Pulsestorm_HelloWorldMVVM" />
        </route>
    </router>
</config>

To provide more information about this, id is standard when you want this for store view, and if you want it for admin panel, it would be admin. Route id and frontname will almost always be the smae.

Creating a controller would give you to access the second and third url segments. In this case, we will use hello and world. The php file would be inside Pulsestorm\HelloWorldMVVM\Controller\Hello\World.php. So that the url for this action would be baseurl\hello_mvvm\hello\world. We can see that it executes by setting an execute function to echo out anything.. maybe something like this Note that the function will have only one entry point, the execute function, for collaboration and ease confusion. Also, we should set the namespace on the php file or else the routing will not work.

// app/code/Pulsestorm/HelloWorld/Controller/Hello/World.php

<?php
namespace Pulsestorm\HelloWorld\Controller\Hello;
class World extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {
        echo '<p>you did it!</p>';
        var_dump(__METHOD__);
    }
}

We can assure that the World php file is on track, but what we want is not just you did it prints, but actual page layouts. You can do this by using Magento 2’s automatic constructor dependency injection. It would look like this.

// app/code/Pulsestorm/HelloWorldMVVM/Controller/Hello/World.php

<?php
namespace Pulsestorm\HelloWorldMVVM\Controller\Hello;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Context;

class World extends \Magento\Framework\App\Action\Action
{
    protected $pageFactory;
    public function __construct(Context $context, PageFactory $pageFactory)
    {
        $this->pageFactory = $pageFactory;
        return parent::__construct($context); 
    }

    public function execute()
    {
        var_dump(__METHOD__); // only for testing method
        $page_object = $this->pageFactory->create();
        return $page_object;
    }

}

And this will give us a page layout. After this layout, we should set up the contents in the layout. To do this, we should be able to set up a full action name layout handle XML file. These XML files lets Magento know that for a certain page, what files it should handle. The title of this XML file is also important. It should be a combination of underscores with the route_id(which should almost always similar to front_name) in routes.xml, and the two action name. The xml file’s name should therefore be hello_mvvm_hello_world.xml in our case, and be sure to make it all lowercase or else it will not work. It should be in app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml`

// app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml 

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" 
    xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <referenceBlock name="content">
        <block
            template="content.phtml"
            class="Pulsestorm\HelloWorldMVVM\Block\Main"
            name="pulsestorm_helloworld_mvvm" />
    </referenceBlock>
</page>

Here, the name would be the block name, and it wouldn’t have to be specifically restricted, But it should be globally unique, and as we can access it from other codeto get a reference to our block object, it should be name related. Apart from that, we could set whatever we want and it will not affect the functionality of the program. For Template, it would be in app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml and you could also specify this if you have the template file in subfolder.

The block class will be made using Pulsestorm\HelloWorldMVVM\Block\Main. This would be a view model of the MVVM system, and the phtml will be the view. It would set the logic a little away from the view, It just brings one more layer just so that setting up and getting is on the other file. The block file would look like this.

// app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php

<?php
namespace Pulsestorm\HelloWoldMVVM\Block;
use Magento\Framework\View\Element\Template;

class Main extends Template
{
    protected function _prepareLayout()
    {
        $this->setMessage('hello~~~');
        $this->setName($this->getRequest()->getParam('name'));

    }

    public function getSomething()
    {
         return "something";
    }
}

Actually, you can just have an empty _prepareLayout function and the output will be just fine. Finally, we need a template, which would be a phtml file, a combination of php and html. It surely has to be the name of content.phtml, and it should be in the folder of app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml. We can write any html, such as <h1>hello world</h1> or whatever, but as we have set variables above, we should use them such as

<h1>
  <?php echo $this->escapeHtml($this->getMessage());  ?>
  <?php echo $this->escapeHtml($this->getName());  ?>
</h1>
<h2>
<?php echo $this->escapeHtml($this->getGoodbyeMessage()); ?>
</h2>