Page.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\View\Result;
  7. use Magento\Framework;
  8. use Magento\Framework\App\Response\HttpInterface as HttpResponseInterface;
  9. use Magento\Framework\View;
  10. /**
  11. * A "page" result that encapsulates page type, page configuration
  12. * and imposes certain layout handles.
  13. *
  14. * The framework convention is that there will be loaded a guaranteed handle for "all pages",
  15. * then guaranteed handle that corresponds to page type
  16. * and a guaranteed handle that stands for page layout (a wireframe of a page)
  17. *
  18. * Page result is a more specific implementation of a generic layout response
  19. *
  20. * @SuppressWarnings(PHPMD.TooManyFields)
  21. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  22. * @SuppressWarnings(PHPMD.DepthOfInheritance)
  23. *
  24. * @api
  25. * @since 100.0.2
  26. */
  27. class Page extends Layout
  28. {
  29. /**
  30. * @var string
  31. */
  32. protected $pageLayout;
  33. /**
  34. * @var \Magento\Framework\View\Page\Config
  35. */
  36. protected $pageConfig;
  37. /**
  38. * @var \Magento\Framework\View\Page\Config\RendererInterface
  39. */
  40. protected $pageConfigRenderer;
  41. /**
  42. * @var \Magento\Framework\View\Page\Config\RendererFactory
  43. */
  44. protected $pageConfigRendererFactory;
  45. /**
  46. * @var \Magento\Framework\View\Page\Layout\Reader
  47. */
  48. protected $pageLayoutReader;
  49. /**
  50. * @var \Magento\Framework\View\FileSystem
  51. */
  52. protected $viewFileSystem;
  53. /**
  54. * @var array
  55. */
  56. protected $viewVars;
  57. /**
  58. * @var string
  59. */
  60. protected $template;
  61. /**
  62. * @var Framework\App\RequestInterface
  63. */
  64. protected $request;
  65. /**
  66. * Asset service
  67. *
  68. * @var \Magento\Framework\View\Asset\Repository
  69. */
  70. protected $assetRepo;
  71. /**
  72. * @var \Psr\Log\LoggerInterface
  73. */
  74. protected $logger;
  75. /**
  76. * @var Framework\UrlInterface
  77. */
  78. protected $urlBuilder;
  79. /**
  80. * @var View\EntitySpecificHandlesList
  81. */
  82. private $entitySpecificHandlesList;
  83. /**
  84. * Constructor
  85. *
  86. * @param View\Element\Template\Context $context
  87. * @param View\LayoutFactory $layoutFactory
  88. * @param View\Layout\ReaderPool $layoutReaderPool
  89. * @param Framework\Translate\InlineInterface $translateInline
  90. * @param View\Layout\BuilderFactory $layoutBuilderFactory
  91. * @param View\Layout\GeneratorPool $generatorPool
  92. * @param View\Page\Config\RendererFactory $pageConfigRendererFactory
  93. * @param View\Page\Layout\Reader $pageLayoutReader
  94. * @param string $template
  95. * @param bool $isIsolated
  96. * @param View\EntitySpecificHandlesList $entitySpecificHandlesList
  97. *
  98. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  99. */
  100. public function __construct(
  101. View\Element\Template\Context $context,
  102. View\LayoutFactory $layoutFactory,
  103. View\Layout\ReaderPool $layoutReaderPool,
  104. Framework\Translate\InlineInterface $translateInline,
  105. View\Layout\BuilderFactory $layoutBuilderFactory,
  106. View\Layout\GeneratorPool $generatorPool,
  107. View\Page\Config\RendererFactory $pageConfigRendererFactory,
  108. View\Page\Layout\Reader $pageLayoutReader,
  109. $template,
  110. $isIsolated = false,
  111. View\EntitySpecificHandlesList $entitySpecificHandlesList = null
  112. ) {
  113. $this->request = $context->getRequest();
  114. $this->assetRepo = $context->getAssetRepository();
  115. $this->logger = $context->getLogger();
  116. $this->urlBuilder = $context->getUrlBuilder();
  117. $this->pageConfig = $context->getPageConfig();
  118. $this->pageLayoutReader = $pageLayoutReader;
  119. $this->viewFileSystem = $context->getViewFileSystem();
  120. $this->pageConfigRendererFactory = $pageConfigRendererFactory;
  121. $this->template = $template;
  122. $this->entitySpecificHandlesList = $entitySpecificHandlesList
  123. ?: \Magento\Framework\App\ObjectManager::getInstance()->get(View\EntitySpecificHandlesList::class);
  124. parent::__construct(
  125. $context,
  126. $layoutFactory,
  127. $layoutReaderPool,
  128. $translateInline,
  129. $layoutBuilderFactory,
  130. $generatorPool,
  131. $isIsolated
  132. );
  133. $this->initPageConfigReader();
  134. }
  135. /**
  136. * Initialize page config reader
  137. *
  138. * @return void
  139. */
  140. protected function initPageConfigReader()
  141. {
  142. $this->pageConfigRenderer = $this->pageConfigRendererFactory->create(['pageConfig' => $this->pageConfig]);
  143. }
  144. /**
  145. * Create layout builder
  146. *
  147. * @return void
  148. */
  149. protected function initLayoutBuilder()
  150. {
  151. $this->layoutBuilderFactory->create(View\Layout\BuilderFactory::TYPE_PAGE, [
  152. 'layout' => $this->layout,
  153. 'pageConfig' => $this->pageConfig,
  154. 'pageLayoutReader' => $this->pageLayoutReader
  155. ]);
  156. }
  157. /**
  158. * Set up default handles for current page
  159. *
  160. * @return $this
  161. */
  162. public function initLayout()
  163. {
  164. $this->addHandle('default');
  165. $this->addHandle($this->getDefaultLayoutHandle());
  166. $update = $this->getLayout()->getUpdate();
  167. if ($update->isLayoutDefined()) {
  168. $update->removeHandle('default');
  169. }
  170. return $this;
  171. }
  172. /**
  173. * Add default handle
  174. *
  175. * @return $this
  176. */
  177. public function addDefaultHandle()
  178. {
  179. $this->addHandle('default');
  180. return parent::addDefaultHandle();
  181. }
  182. /**
  183. * Return page configuration
  184. *
  185. * @return \Magento\Framework\View\Page\Config
  186. */
  187. public function getConfig()
  188. {
  189. return $this->pageConfig;
  190. }
  191. /**
  192. * Add layout updates handles associated with the action page
  193. *
  194. * @param array|null $parameters page parameters
  195. * @param string|null $defaultHandle
  196. * @param bool $entitySpecific
  197. * @return bool
  198. */
  199. public function addPageLayoutHandles(array $parameters = [], $defaultHandle = null, $entitySpecific = true)
  200. {
  201. $handle = $defaultHandle ? $defaultHandle : $this->getDefaultLayoutHandle();
  202. $pageHandles = [$handle];
  203. foreach ($parameters as $key => $value) {
  204. $pageHandle = $handle . '_' . $key . '_' . $value;
  205. $pageHandles[] = $pageHandle;
  206. if ($entitySpecific) {
  207. $this->entitySpecificHandlesList->addHandle($pageHandle);
  208. }
  209. }
  210. // Do not sort array going into add page handles. Ensure default layout handle is added first.
  211. $this->addHandle($pageHandles);
  212. return true;
  213. }
  214. /**
  215. * {@inheritdoc}
  216. */
  217. protected function render(HttpResponseInterface $response)
  218. {
  219. $this->pageConfig->publicBuild();
  220. if ($this->getPageLayout()) {
  221. $config = $this->getConfig();
  222. $this->addDefaultBodyClasses();
  223. $addBlock = $this->getLayout()->getBlock('head.additional'); // todo
  224. $requireJs = $this->getLayout()->getBlock('require.js');
  225. $this->assign([
  226. 'requireJs' => $requireJs ? $requireJs->toHtml() : null,
  227. 'headContent' => $this->pageConfigRenderer->renderHeadContent(),
  228. 'headAdditional' => $addBlock ? $addBlock->toHtml() : null,
  229. 'htmlAttributes' => $this->pageConfigRenderer->renderElementAttributes($config::ELEMENT_TYPE_HTML),
  230. 'headAttributes' => $this->pageConfigRenderer->renderElementAttributes($config::ELEMENT_TYPE_HEAD),
  231. 'bodyAttributes' => $this->pageConfigRenderer->renderElementAttributes($config::ELEMENT_TYPE_BODY),
  232. 'loaderIcon' => $this->getViewFileUrl('images/loader-2.gif'),
  233. ]);
  234. $output = $this->getLayout()->getOutput();
  235. $this->assign('layoutContent', $output);
  236. $output = $this->renderPage();
  237. $this->translateInline->processResponseBody($output);
  238. $response->appendBody($output);
  239. } else {
  240. parent::render($response);
  241. }
  242. return $this;
  243. }
  244. /**
  245. * Add default body classes for current page layout
  246. *
  247. * @return $this
  248. */
  249. protected function addDefaultBodyClasses()
  250. {
  251. $this->pageConfig->addBodyClass($this->request->getFullActionName('-'));
  252. $pageLayout = $this->getPageLayout();
  253. if ($pageLayout) {
  254. $this->pageConfig->addBodyClass('page-layout-' . $pageLayout);
  255. }
  256. return $this;
  257. }
  258. /**
  259. * @return string
  260. */
  261. protected function getPageLayout()
  262. {
  263. return $this->pageConfig->getPageLayout() ?: $this->getLayout()->getUpdate()->getPageLayout();
  264. }
  265. /**
  266. * Assign variable
  267. *
  268. * @param string|array $key
  269. * @param mixed $value
  270. * @return $this
  271. */
  272. protected function assign($key, $value = null)
  273. {
  274. if (is_array($key)) {
  275. foreach ($key as $subKey => $subValue) {
  276. $this->assign($subKey, $subValue);
  277. }
  278. } else {
  279. $this->viewVars[$key] = $value;
  280. }
  281. return $this;
  282. }
  283. /**
  284. * Render page template
  285. *
  286. * @return string
  287. * @throws \Exception
  288. */
  289. protected function renderPage()
  290. {
  291. $fileName = $this->viewFileSystem->getTemplateFileName($this->template);
  292. if (!$fileName) {
  293. throw new \InvalidArgumentException('Template "' . $this->template . '" is not found');
  294. }
  295. ob_start();
  296. try {
  297. extract($this->viewVars, EXTR_SKIP);
  298. include $fileName;
  299. } catch (\Exception $exception) {
  300. ob_end_clean();
  301. throw $exception;
  302. }
  303. $output = ob_get_clean();
  304. return $output;
  305. }
  306. /**
  307. * Retrieve url of a view file
  308. *
  309. * @param string $fileId
  310. * @param array $params
  311. * @return string
  312. */
  313. protected function getViewFileUrl($fileId, array $params = [])
  314. {
  315. try {
  316. $params = array_merge(['_secure' => $this->request->isSecure()], $params);
  317. return $this->assetRepo->getUrlWithParams($fileId, $params);
  318. } catch (\Magento\Framework\Exception\LocalizedException $e) {
  319. $this->logger->critical($e);
  320. return $this->urlBuilder->getUrl('', ['_direct' => 'core/index/notFound']);
  321. }
  322. }
  323. }