Primeiramente meu olá à você que está lendo este artigo.
Acredito que se você chegou até aqui esteja interessado em aprender um pouco mais sobre o Magento 2. Pois bem, eu também estou interessadíssimo em aprender sempre mais sobre o Magento 2.
O Magento 1 se tornou, entre todos estes anos, a principal plataforma de e-commerce do mercado mundial e isso chamou muito a atenção de nós, desenvolvedores, e de profissionais que trabalham, direta ou indiretamente, com o Magento. Chamou tanto a atenção que agora o mercado está se preparando para usar fortemente a nova versão do Magento Commerce, a versão 2, e nós, desenvolvedores, precisamos estar sempre atualizados sobre as novidades em torno do Magento para podermos utilizá-las sempre que necessário, seja para um freela ou para um projeto em sua empresa.
Pelo pouco que estudei a plataforma pude perceber que ela é simplesmente sensacional em alguns quesitos o muito atualizada em relação ás plataformas do mercado atualmente. Muitas das novidades do mercado de desenvolvimento web estão implementadas na plataforma como, por exemplo, o uso de composer para gerenciar as dependências PHP do seu projeto, a utilização dos namespaces, disponíveis desde a versão 5.3 do PHP, a inclusão de jQuery como biblioteca de javascript nativa, a possibilidade da utilização das Traits, disponíveis desde a versão 5.4 do PHP, enfim, se você quiser saber um pouco mais sobre algumas das diferenças, você pode ler este post.
Neste post quero falar apenas sobre o Request Flow do Magento 2.
O que é Request Flow?
Request Flow, ou em português Fluxo de Requisição, que também pode ser conhecido como Application Bootstrap, nada mais é do que o fluxo da requisição dentro da aplicação, ou seja, é todo o processo que ocorre dentro da plataforma desde quando ela recebe a requisição (chamada de URL) pelo arquivo index.php, responsável por inicializar a aplicação, até o momento que o usuário pode ver o resultado da página no navegador.
Aonde Tudo Começa
Como dito logo acima, assim como é em várias outras plataformas e frameworks web, tudo se inicia pelo arquivo index.php:
| <?php | |
| /** | |
| * Application entry point | |
| * | |
| * Example - run a particular store or website: | |
| * -------------------------------------------- | |
| * require __DIR__ . '/app/bootstrap.php'; | |
| * $params = $_SERVER; | |
| * $params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = 'website2'; | |
| * $params[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website'; | |
| * $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $params); | |
| * \/** @var \Magento\Framework\App\Http $app *\/ | |
| * $app = $bootstrap->createApplication('Magento\Framework\App\Http'); | |
| * $bootstrap->run($app); | |
| * -------------------------------------------- | |
| * | |
| * Copyright © 2015 Magento. All rights reserved. | |
| * See COPYING.txt for license details. | |
| */ | |
| try { | |
| require __DIR__ . '/app/bootstrap.php'; | |
| } catch (\Exception $e) { | |
| echo <<<HTML | |
| <div style="font:12px/1.35em arial, helvetica, sans-serif;"> | |
| <div style="margin:0 0 25px 0; border-bottom:1px solid #ccc;"> | |
| <h3 style="margin:0;font-size:1.7em;font-weight:normal;text-transform:none;text-align:left;color:#2f2f2f;"> | |
| Autoload error</h3> | |
| </div> | |
| <p>{$e->getMessage()}</p> | |
| </div> | |
| HTML; | |
| exit(1); | |
| } | |
| $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER); | |
| /** @var \Magento\Framework\App\Http $app */ | |
| $app = $bootstrap->createApplication('Magento\Framework\App\Http'); | |
| $bootstrap->run($app); |
Perceba que no arquivo index.php, mais precisamente na linha 22, é chamado o arquivo app/bootstrap.php que tem a responsabilidade de incluir os arquivos app/autoload.php e app/functions.php e configurar o timezone padrão do Magento para UTC:
| <?php | |
| /** | |
| * Copyright © 2015 Magento. All rights reserved. | |
| * See COPYING.txt for license details. | |
| */ | |
| /** | |
| * Environment initialization | |
| */ | |
| error_reporting(E_ALL); | |
| #ini_set('display_errors', 1); | |
| umask(0); | |
| /* PHP version validation */ | |
| if (version_compare(phpversion(), '5.5.0', '<') === true) { | |
| if (PHP_SAPI == 'cli') { | |
| echo 'Magento supports PHP 5.5.0 or later. ' . | |
| 'Please read http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html'; | |
| } else { | |
| echo <<<HTML | |
| <div style="font:12px/1.35em arial, helvetica, sans-serif;"> | |
| <p>Magento supports PHP 5.5.0 or later. Please read | |
| <a target="_blank" href="http://devdocs.magento.com/guides/v1.0/install-gde/system-requirements.html"> | |
| Magento System Requirements</a>. | |
| </div> | |
| HTML; | |
| } | |
| exit(1); | |
| } | |
| require_once __DIR__ . '/autoload.php'; | |
| require_once BP . '/app/functions.php'; | |
| if (!empty($_SERVER['MAGE_PROFILER']) | |
| && isset($_SERVER['HTTP_ACCEPT']) | |
| && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false | |
| ) { | |
| \Magento\Framework\Profiler::applyConfig( | |
| $_SERVER['MAGE_PROFILER'], | |
| BP, | |
| !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' | |
| ); | |
| } | |
| date_default_timezone_set('UTC'); |
O arquivo app/autoload.php tem a responsabilidade de fazer a inclusão de todos os arquivos necessários para o autoload das classes dentro da pasta vendor para que tudo funcione corretamente no momento de instanciar novos objetos. Já o arquivo app/functions.php é responsável por adicionar o método de tradução __ (isso mesmo, são dois underscores) que tanto conhecemos no Magento 1.
Com o autoload inicializado, o próximo passo é criar uma instância do bootstrap:
... $bootstrap = MagentoFrameworkAppBootstrap::create(BP, $_SERVER); ...
Esta chamada faz com que o ObjectManager do Magento seja iniciado. Não entrarei em detalhes sobre o que é o ObjectManager no Magento 2 neste post, vale á pena dedicar um post só para isso, e é o que farei.
O próximo passo é criar uma instância da classe MagentoFrameworkAppHttp, substituto da classe Mage_Core_Model_App do Magento 1, e chamar o método run, passando como referência a classe MagentoFrameworkAppHttp criada:
...
/** @var MagentoFrameworkAppHttp $app */
$app = $bootstrap->createApplication('MagentoFrameworkAppHttp');
$bootstrap->run($app);
...
Agora todo o controle da inicialização da aplicação está nas mãos da classe MagentoFrameworkAppBootstrap.
| <?php | |
| ... | |
| /** | |
| * Runs an application | |
| * | |
| * @param \Magento\Framework\AppInterface $application | |
| * @return void | |
| */ | |
| 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); | |
| } | |
| } | |
| ... |
Este método segue os seguintes passos:
Inicializa um profiler:
...
MagentoFrameworkProfiler::start('magento');
...
Inicializa a classe responsável por tratamento de erros:
... $this->initErrorHandler(); ...
Inicializa o ObjectManager, caso o mesmo não esteja inicializado:
... $this->initObjectManager(); ...
Verifica se o Magento está em estado de manutenção:
... $this->assertMaintenance(); ...
Verifica se o Magento já está instalado e se a instalação está correta:
... $this->assertInstalled(); ...
E finalmente inicia a aplicação:
... $response = $application->launch(); ...
Podemos ver o método launch() melhor aqui:
| <?php | |
| ... | |
| /** | |
| * Run application | |
| * | |
| * @throws \InvalidArgumentException | |
| * @return ResponseInterface | |
| */ | |
| 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; | |
| } | |
| ... |
Neste método, se tratando do principal do request flow, duas linhas se destacam:
...
/** @var MagentoFrameworkAppFrontControllerInterface $frontController */
$frontController = $this-&amp;amp;amp;amp;amp;amp;gt;_objectManager-&amp;amp;amp;amp;amp;amp;gt;get('MagentoFrameworkAppFrontControllerInterface');
$result = $frontController-&amp;amp;amp;amp;amp;amp;gt;dispatch($this-&amp;amp;amp;amp;amp;amp;gt;_request);
...
Estas linhas são responsáveis por instanciar a classe MagentoFrameworkAppFrontController, localizada em lib/internal/Magento/Framework/App/FrontController.php e chamar o método dispatch() da mesma:
| <?php | |
| /** | |
| * Front controller responsible for dispatching application requests | |
| * | |
| * Copyright © 2015 Magento. All rights reserved. | |
| * See COPYING.txt for license details. | |
| */ | |
| namespace Magento\Framework\App; | |
| class FrontController implements FrontControllerInterface | |
| { | |
| /** | |
| * @var RouterList | |
| */ | |
| protected $_routerList; | |
| /** | |
| * @var \Magento\Framework\App\Response\Http | |
| */ | |
| protected $response; | |
| /** | |
| * @param RouterList $routerList | |
| * @param \Magento\Framework\App\Response\Http $response | |
| */ | |
| public function __construct( | |
| RouterList $routerList, | |
| \Magento\Framework\App\Response\Http $response | |
| ) { | |
| $this->_routerList = $routerList; | |
| $this->response = $response; | |
| } | |
| /** | |
| * Perform action and generate response | |
| * | |
| * @param RequestInterface $request | |
| * @return ResponseInterface|\Magento\Framework\Controller\ResultInterface | |
| * @throws \LogicException | |
| */ | |
| 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; | |
| } | |
| } 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; | |
| } | |
| } |
Routers
Um Router, em poucas palavras, é uma classe responsável por analisar e processar uma requisição. Assim como no Magento 1, no Magento 2 temos, por padrão, alguns Routers configurados para a aplicação funcionar: Base Router, Default Router, Cms Router e UrlRewrite Router. Vamos falar um pouco sobre seus propósitos e como cada uma funciona. Basicamente a sequência de leitura dos routers é:
Base Router → CMS Router → UrlRewrite Router → Default Router
Base Router
Localizado em lib/internal/Magento/Framework/App/Router/Base.php, é o primeiro router do loop e se você é um desenvolvedor Magento 1 saberá que este é basicamente o Standard Router, responsável por rotear as rotas standards, ou seja, as rotas definidas para o frontend da aplicação, como por exemplo a roda customer/account/login.
CMS Router
Localizada em app/code/Magento/Cms/Controller/Router.php, é utilizada para gerenciar as rotas das páginas CMS, e definir o nome do módulo para cms, nome do controller para page e o nome da action para view – app/code/Magento/Cms/Controller/Page/View.php. Após estas definições, irá definir o parâmetro page_id e então encaminhar a requisição de volta para o Base Router para que o mesmo faça o dispatch da rota corretamente. Isso devido o Base Router só conseguir mapear corretamente rotas no formato modulo/controller/view, portanto quem precisa deixar a rota neste formato é o CMS Router.
UrlRewrite Router
No Magento 2 o UrlRewrite tem seu próprio Router, se você já conhece o Magento 1 saberá que URL Rewrite fazia parte do Standard Router. Está localizado em app/code/Magento/UrlRewrite/Controller/Router.php e utiliza o Url Finder para definir as rotas de URL Rewrite do banco dados. Após a definição da rota, assim como o CMS Router, O UrlRewrite Router faz um forward da requisição para que o Base Router faça o dispatch.
Default Router
Está localizado em lib/internal/Magento/Framework/App/Router/DefaultRouter.php e é o último router do laço. É utilizado apenas quando nenhum dos outros routers conseguem fazer o match da rota. No Magento 2 nós podemos criar handles personalizados para páginas “Não encontradas” e assim mostrar um conteúdo personalizado.
Dispatch de Routers
Quando o Magento chega neste ponto da aplicação, os Routers são iterados e cada um deles irá tentar fazer o dispatch da rota corretamente, ou seja, cada um com sua responsabilidade, tentará encontrar o Controller Action responsável pela lógica da regra de negócio daquela requisição, por exemplo, quando a rota chamada for customer/account/login o Controller Action responsável pela regra de negócio será MagentoCustomerControllerAccountLogin, localizada em app/code/Magento/Customer/Controller/Account/Login.php.
A partir deste momento o fluxo da requisição fica nas mãos do Controller Action do módulo responsável, conforme o exemplo acima, e o mesmo tem como responsabilidade de entregar o resultado da requisição. Podemos ver que o resultado é salvo em uma variável $result que é uma instância da classe MagentoFrameworkViewResultPage que, por sua vez, extende a classe MagentoFrameworkViewResultLayout. Quem for desenvolvedor Magento 1 saberá que este objeto é responsável por fazer a renderização dos Layout Blocks, que nada mais são do que o conteúdo da página montada como um quebra-cabeça.
Então o método renderResult é chamado para fazer a renderização dos blocos:
...
/** @class MagentoFrameworkViewResultLayout **/
/**
* Render current layout
*
* @param ResponseInterface $response
* @return $this
*/
public function renderResult(ResponseInterface $response)
{
MagentoFrameworkProfiler::start('LAYOUT');
MagentoFrameworkProfiler::start('layout_render');
$this->applyHttpHeaders($response);
$this->render($response);
$this->eventManager->dispatch('layout_render_before');
$this->eventManager->dispatch('layout_render_before_' . $this->request->getFullActionName());
MagentoFrameworkProfiler::stop('layout_render');
MagentoFrameworkProfiler::stop('LAYOUT');
return $this;
}
...
Retornando o objeto MagentoFrameworkAppResponseHttp para o método MagentoFrameworkAppBootstrap::run() (lembra dele?) e o mesmo simplesmente envia a resposta ao navegador do usuário:
/* ... */
public function run(AppInterface $application)
{
/* ... */
$response->sendResponse();
/* ... */
}
/* ... */
Este é o fluxo base do Request Flow do Magento 2. É realmente bastante parecido com o Request Flow do Magento 1, com conceitos muito próximos.
Aproveitem para dar uma olhada no código e estudar um pouco mais sobre o Magento 2.
Grande abraço,
– Tiago
Olá tiago olhei sua pagina acredito que possa me ajudar
Hoje estou montando minha loja magento V 2.1.4 e esta neste provisorio dominio . americanbrasil.lpcdev.com.br que é de um desenvolvedor .
Estou com algumas dificuldades em encontrar APi Correios que funcione corretamente e Demais Api .
Será que com sua experiencia poderia me indicar algum local onde eu possa encontrar varias Api podem ser pagas não tem problema .
Preciso de um ERP tambem , aguardo desde já suas respostas .
Att
ilcimar Canibal
LikeLike
Olá Ilcimar, tudo bem?
Acredito que este link possa lhe ajudar: Correios Offline & Online.
Abraços,
– Tiago
LikeLike