Book Image

Magento 2 Development Quick Start Guide

By : Branko Ajzele
Book Image

Magento 2 Development Quick Start Guide

By: Branko Ajzele

Overview of this book

Magento is an open-source, enterprise-level e-commerce platform with unlimited scope for customization. This makes it a great choice not only for vendors but for developers as well. This book guides you through Magento development, teaching you how to develop modules that extend or change its functionality, leading to more ?exible and profitable Magento stores. You start with a structural overview of the key Magento development components. You will learn where things such as plugins, events, models, controllers, layouts, and UI components ft into the development landscape. You will go through examples of using these components to extend Magento. As you progress, you will be building a diverse series of small but practical Magento modules. By the end of this book, you will not only have a solid foundation in the Magento development architecture; but you will also have practical experience in developing modules to customize and extend Magento stores.
Table of Contents (11 chapters)

Request flow processing

URLs in Magento have the format of <AreaFrontName>/<VendorName>/<ModuleName>/<ControllerName>/<ActionName>, but this does not mean that we actually use the area, vendor, or module name in the URL any time we wish to access a certain controller. For example, the area for a request is defined by the first request path segment, such as admin for adminhtml area, and none for frontend area.

We use the router class to assign a URL to a corresponding controller and its action. The router's match method finds a matching controller, which is determined by an incoming request.

Conceptually, creating a new router is as simple as doing the following:

  1. Inject the new item under the routerList argument of the Magento\Framework\App\RouterList type via the di.xml file.
  2. Create a router file (by using the match method, which implements \Magento\Framework\App\RouterInterface).
  3. Return an instance of \Magento\Framework\App\ActionInterface.

By doing a lookup for the name="routerList" string across all of the <MAGENTO_DIR> di.xml files, we can see the following router definitions:

  • Magento\Robots\Controller\Router (robots)
  • Magento\Cms\Controller\Router (cms)
  • Magento\UrlRewrite\Controller\Router (urlrewrite)
  • Magento\Framework\App\Router\Base (standard)
  • Magento\Framework\App\Router\DefaultRouter (default)
  • Magento\Backend\App\Router (admin)

Let's take a closer look at the robots router under <MAGENTO_DIR>/module-robots. etc/frontend/di.xml injects the new item under the routerList argument as follows:

<type name="Magento\Framework\App\RouterList">
<arguments>
<argument name="routerList" xsi:type="array">
<item name="robots" xsi:type="array">
<item name="class" xsi:type="string">Magento\Robots\Controller\Router</item>
<item name="disable" xsi:type="boolean">false</item>
<item name="sortOrder" xsi:type="string">10</item>
</item>
</argument>
</arguments>
</type>

The Magento\Robots\Controller\Router class has been further defined as per the following partial extract:

class Router implements \Magento\Framework\App\RouterInterface {
// Magento\Framework\App\ActionFactory
private $actionFactory;
// Magento\Framework\App\Router\ActionList
private $actionList;
// Magento\Framework\App\Route\ConfigInterface
private $routeConfig;

public function match(\Magento\Framework\App\RequestInterface $request) {
$identifier = trim($request->getPathInfo(), '/');
if ($identifier !== 'robots.txt') {
return null;
}

$modules = $this->routeConfig->getModulesByFrontName('robots');
if (empty($modules)) {
return null;
}

$actionClassName = $this->actionList->get($modules[0], null, 'index', 'index');
$actionInstance = $this->actionFactory->create($actionClassName);
return $actionInstance;
}
}

The match method checks if the robots.txt file was requested and returns the instance of the matched \Magento\Framework\App\ActionInterface type. By following this simple implementation, we can easily create the route of our own.

Conceptually, creating a new controller is as simple as doing the following:

  1. Register a route via router.xml.
  2. Create an abstract controller file (as an abstract class, which extends \Magento\Framework\App\Action\Action).
  1. Create an action controller file (which extends the main controller file with the execute method, and implements \Magento\Framework\App\ActionInterface).
  2. Return an instance of \Magento\Framework\Controller\ResultInterface.
The separation of the controller into the main and action controller files is not a technical requirement, but rather a recommended organizational one. Magento does this across the majority of its modules.

By doing a lookup for the <route string across the <MAGENTO_DIR> routes.xml files, we can see that Magento uses hundreds of route definitions, which are spread across its modules. Each route represents one controller.

Let's take a closer look at one of Magento's controllers, <MAGENTO_DIR>/module-customer, which maps to the http://magelicious.loc/customer/address/form URL. The route itself is registered via frontend/di.xml under the standard router with a customer ID and a customer frontName, as follows:

<router id="standard">
<route id="customer" frontName="customer">
<module name="Magento_Customer" />
</route>
</router>

The abstract controller file Controller/Address.php is defined partially as follows:

abstract class Address extends \Magento\Framework\App\Action\Action {
// The rest of the code...
}

The abstract controller is where we want to add functionality and dependencies that are shared across all of the child action controllers.

We can further see three different action controllers defined within the subdirectory which has the same name as the abstract class. The Controller/Address directory contains six action controllers: Delete.php, Edit.php, Form.php, FormPost.php, Index.php, and NewAction.php. Let's take a closer look at the following partial Form.php file's content:

class Form extends \Magento\Customer\Controller\Address {
public function execute() {
/** @var \Magento\Framework\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$navigationBlock = $resultPage->getLayout()->getBlock('customer_account_navigation');
if ($navigationBlock) {
$navigationBlock->setActive('customer/address');
}
return $resultPage;
}
}

The example here uses the create method of the injected Magento\Framework\View\Result\PageFactory type to create a new page result. The various types of controller results can be found within the <MAGENTO_DIR>/framework directory:

  • Magento\Framework\Controller\Result\Json
  • Magento\Framework\Controller\Result\Raw
  • Magento\Framework\Controller\Result\Redirect
  • Magento\Framework\Controller\Result\Forward
  • Magento\Framework\View\Result\Layout
  • Magento\Framework\View\Result\Page

We can take the unified way of creating result instances by using the create method of \Magento\Framework\Controller\ResultFactory. The ResultFactory defines the TYPE_* constant for each of the mentioned controller result types:

const TYPE_JSON = 'json';
const TYPE_RAW = 'raw';
const TYPE_REDIRECT = 'redirect';
const TYPE_FORWARD = 'forward';
const TYPE_LAYOUT = 'layout';
const TYPE_PAGE = 'page';

Keeping these constants in mind, we can easily write our action controller code as follows:

$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
$resultRedirect->setPath('adminhtml/*/index');
return $resultRedirect;
A quick lookup for the $this->resultFactory-> create string, across the entire <MAGENTO_DIR> directory, can give us lots of examples of how to use the ResultFactory for our own code.