The physical process of rendering is conceptually the same as in Magento 1, but it differs from the code and architectural levels. Data meant for output to the browser accumulates in the controller in the internal variable Zend\Stdlib\Message::content, and is displayed by calling Magento\Framework\HTTP\PhpEnvironment::appendBody().
In general terms, the following steps are taken during the rendering and flushing output process:
- Custom controller returns an object of the required type.
- The front controller (Magento\Framework\App-\FrontController) dispatches a request and gets a result object.
- The app (Magento\Framework\App\Http) in the launch() method copies HTML to the response object.
- The bootstrap (Magento\Framework\App\Bootstrap) flushes that HTML from the response objects to the browser.
1.1 PageObject can be injected into the controller, created, and returned from the execute() method:
1 2 3 4 5 6 7 8 9 |
/Import page factory into constructor: public function __construct(\Magento\Framework\View\Result\PageFactory $pageFactory, ...) { $this->pageFactory = $pageFactory; ... } //Create and return object in execute method $page = $this->pageFactory->create(); return $page; |
1.2 JSON result object, created in the execute() method, can be injected into a controller.
Usually a controller returns an object as the result of the call $result->setData($data), which is a $result object. The setData() method is used to assign the array to return as JSON.
1 2 3 4 5 6 7 8 9 |
//Import json factory into constructor: public function __construct(\Magento\Framework\Controller\Result\JsonFactory $jsonFactory, ...) { $this->jsonFactory = $jsonFactory; ... } //Create json object in the execute method; set data into it, and return $result = $this->jsonFactory->create(); return $result->setData($data); |
Magento 2 Development
Take your online store to the next level with BelVG Magento 2 Development
Visit the page2. Result objects are got back to the FrontController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$result = $actionInstance->execute();): 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); $this->response->setNoCacheHeaders(); if ($actionInstance instanceof \Magento\Framework\App\Action\AbstractAction) { $result = $actionInstance->dispatch($request); } else { $result = $actionInstance->execute(); } break; } .... return $result; |
3. And then go to the App object (Magento\Framework\App\Http), to the launch() method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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; } |
The event controller_front_send_response_before may contain additional headers or params.
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.$result->renderResult($this->_response) – the exact place where rendering output occurs.
4. Magento will generate HTML and append it to the response object. Response objects are then got back to the Bootstrap class (run() method) before being flushed:
1 2 3 4 5 6 7 8 9 10 11 12 |
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'); |
Magento\Framework\HTTP\PhpEnvironment\Response::sendResponse()
1 2 3 4 |
public function sendResponse() { $this->send(); } |
Let`s take a close look at Zend\Http\PhpEnvironment::sent() public function:
1 2 3 4 5 6 |
public function send() { $this->sendHeaders() ->sendContent(); return $this; } |
First, the function sendHeaders() applies all the standard headers accumulated through the call Magento\Framework\HTTP\PhpEnvironment\Response::setHeader() and sendContent() – display content body:
1 2 3 4 5 6 7 8 9 10 |
public function sendContent() { if ($this->contentSent()) { return $this; } echo $this->getContent(); $this->:contentSent = true; return $this; } |
Redirect
Magento 2 offers more structure around the forward function and redirects than Magento 1.
There are two types of redirect:
- redirect
Example of redirect result object usage. A redirect header is sent back to the browser, which redirects the browser to another URL:
1 2 3 4 5 6 7 8 9 10 |
//Import redirect factory into constructor public function __construct(\Magento\Framework\Controller\Result\RedirectFactory $redirectFactory, ...) { $this->redirectFactory = $redirectFactory; } //Create redirect result object in the execute method; set data into it, and return $result = $this->redirectFactory->create(); $result->setUrl('some/new/url'); return $result; |
Redirect method – Magento\Framework\App\Action\Action::_redirect():
1 2 3 4 5 |
protected function _redirect($path, $arguments = []) { $this->_redirect->redirect($this->getResponse(), $path, $arguments); return $this->getResponse(); } |
- forward
An example of forward result object usage. An internal redirect invisible to a customer is performed:
1 2 3 4 5 6 7 8 9 10 |
//Import forward factory into constructor public function __construct(\Magento\Framework\Controller\Result\ForwardFactory $forwardFactory, ...) { $this->forwardFactory = $forwardFactory; } //Create and return result object in the execute method: $result = $this->forwardFactory->create(); $result->forward('some/new/route'); return $result; |
Redirect method – Magento\Framework\App\Action\Action::_forward():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
protected function _forward($action, $controller = null, $module = null, array $params = null) { $request = $this->getRequest(); $request->initForward(); if (isset($params)) { $request->setParams($params); } if (isset($controller)) { $request->setControllerName($controller); // Module should only be reset if controller has been specified if (isset($module)) { $request->setModuleName($module); } } $request->setActionName($action); $request->setDispatched(false); } |
It is important to have both forward and redirect actions, and forward and redirect methods. In fact, they are not the same. Why are both actions and methods applied for these functions? Because of the way the routing loop is performed. The controller returns an action instance and requires a true argument, while the action sets it to false within the loop.
Combining Javascript and CSS files
In step 3, when we render output in $result->renderResult($this->_response) method:
1 2 3 4 5 |
public function renderResult(ResponseInterface $response) { $this->applyHttpHeaders($response); return $this->render($response); } |
if $result instance of Magento\Framework\View\Result\Page class:
1 2 3 4 5 6 7 8 9 10 11 12 |
protected function render(ResponseInterface $response) { $this->pageConfig->publicBuild(); if ($this->getPageLayout()) { $config = $this->getConfig(); $this->addDefaultBodyClasses(); $addBlock = $this->getLayout()->getBlock('head.additional'); // todo $requireJs = $this->getLayout()->getBlock('require.js'); $this->assign([ 'requireJs' => $requireJs ? $requireJs->toHtml() : null, 'headContent' => $this->pageConfigRenderer->renderHeadContent(), ... |
There magento2 adds to header content of block with requirejs scripts and $this->pageConfigRenderer->renderHeadContent() add css and js from layout Magento\Framework\View\Page\Config\Renderer::renderHeadContent():
1 2 3 4 5 6 7 8 |
public function renderHeadContent() { ... $result .= $this->renderAssets($this->getAvailableResultGroups()); … } $this->getAvailableResultGroups() - prepare array for three type assets: protected $assetTypeOrder = ['css', 'ico', 'js']; |
Magento\Framework\View\Page\Config\Renderer::renderAssets() – return renderer HTML for all Assets:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public function renderAssets($resultGroups = []) { ... $resultGroups[$type] .= $this->renderAssetGroup($group); ... } protected function renderAssetGroup(\Magento\Framework\View\Asset\PropertyGroup $group) { $groupAssets = $this->processMerge($group->getAll(), $group); $attributes = $this->getGroupAttributes($group); $attributes = $this->addDefaultAttributes( $group->getProperty(GroupedCollection::PROPERTY_CONTENT_TYPE), $attributes ); $groupTemplate = $this->getAssetTemplate( $group->getProperty(GroupedCollection::PROPERTY_CONTENT_TYPE), $attributes ); $groupHtml = $this->renderAssetHtml($groupTemplate, $groupAssets); $groupHtml = $this->processIeCondition($groupHtml, $group); return $groupHtml; |
$this->processMerge($group->getAll(), $group) – call to the assets collection (\Magento\Framework\View\Assets\GropedCollection object) and return array of assets sorted by groups. Depending on the Magento settings CSS and Javascript can be returned separately or combined (STORES->Configuration->Developer->JavaScript Settings->Merge JavaScript Files, STORES->Configuration->Developer->CSS Settings->Merge CSS Files). If the settings are enabled, all JavaScript and CSS files will be combined into one single file.
Finally $this->renderAssetHtml($groupTemplate, $groupAssets) render HTML tag referencing to corresponding URLs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
protected function renderAssetHtml($template, $assets) { $result = ''; try { /** @var $asset \Magento\Framework\View\Asset\AssetInterface */ foreach ($assets as $asset) { $result .= sprintf($template, $asset->getUrl()); } } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->logger->critical($e); $result .= sprintf($template, $this->urlBuilder->getUrl('', ['_direct' => 'core/index/notFound'])); } return $result; } |
Magento 2 Migration
Take your online store to the next level with BelVG Magento 2 Migration
Visit the page