Layout.php 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\View;
  7. use Magento\Framework\App\ObjectManager;
  8. use Magento\Framework\App\State as AppState;
  9. use Magento\Framework\Cache\FrontendInterface;
  10. use Magento\Framework\Event\ManagerInterface;
  11. use Magento\Framework\Exception\LocalizedException;
  12. use Magento\Framework\Message\ManagerInterface as MessageManagerInterface;
  13. use Magento\Framework\Serialize\SerializerInterface;
  14. use Magento\Framework\View\Layout\Element;
  15. use Psr\Log\LoggerInterface as Logger;
  16. /**
  17. * Layout model
  18. *
  19. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  20. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  21. * @SuppressWarnings(PHPMD.TooManyFields)
  22. * @SuppressWarnings(PHPMD.ExcessivePublicCount)
  23. * @SuppressWarnings(PHPMD.TooManyMethods)
  24. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  25. */
  26. class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Framework\View\LayoutInterface
  27. {
  28. /**
  29. * Empty layout xml
  30. */
  31. const LAYOUT_NODE = '<layout/>';
  32. /**
  33. * Layout Update module
  34. *
  35. * @var \Magento\Framework\View\Layout\ProcessorInterface
  36. */
  37. protected $_update;
  38. /**
  39. * Blocks registry
  40. *
  41. * @var array
  42. */
  43. protected $_blocks = [];
  44. /**
  45. * Cache of elements to output during rendering
  46. *
  47. * @var array
  48. */
  49. protected $_output = [];
  50. /**
  51. * Helper blocks cache for this layout
  52. *
  53. * @var array
  54. */
  55. protected $sharedBlocks = [];
  56. /**
  57. * A variable for transporting output into observer during rendering
  58. *
  59. * @var \Magento\Framework\DataObject
  60. */
  61. protected $_renderingOutput;
  62. /**
  63. * Cache of generated elements' HTML
  64. *
  65. * @var array
  66. */
  67. protected $_renderElementCache = [];
  68. /**
  69. * Layout structure model
  70. *
  71. * @var Layout\Data\Structure
  72. */
  73. protected $structure;
  74. /**
  75. * Renderers registered for particular name
  76. *
  77. * @var array
  78. */
  79. protected $_renderers = [];
  80. /**
  81. * Core event manager proxy
  82. *
  83. * @var \Magento\Framework\Event\ManagerInterface
  84. */
  85. protected $_eventManager;
  86. /**
  87. * @var \Magento\Framework\View\Layout\ProcessorFactory
  88. */
  89. protected $_processorFactory;
  90. /**
  91. * @var \Magento\Framework\Message\ManagerInterface
  92. */
  93. protected $messageManager;
  94. /**
  95. * @var bool
  96. */
  97. protected $isPrivate = false;
  98. /**
  99. * @var \Magento\Framework\View\Design\Theme\ResolverInterface
  100. */
  101. protected $themeResolver;
  102. /**
  103. * @var Layout\ReaderPool
  104. */
  105. protected $readerPool;
  106. /**
  107. * @var bool
  108. */
  109. protected $cacheable;
  110. /**
  111. * @var \Magento\Framework\View\Layout\GeneratorPool
  112. */
  113. protected $generatorPool;
  114. /**
  115. * @var \Magento\Framework\View\Layout\BuilderInterface
  116. */
  117. protected $builder;
  118. /**
  119. * @var FrontendInterface
  120. */
  121. protected $cache;
  122. /**
  123. * @var Layout\Reader\ContextFactory
  124. */
  125. protected $readerContextFactory;
  126. /**
  127. * @var Layout\Generator\ContextFactory
  128. */
  129. protected $generatorContextFactory;
  130. /**
  131. * @var Layout\Reader\Context
  132. */
  133. protected $readerContext;
  134. /**
  135. * @var \Magento\Framework\App\State
  136. */
  137. protected $appState;
  138. /**
  139. * @var \Psr\Log\LoggerInterface
  140. */
  141. protected $logger;
  142. /**
  143. * @var SerializerInterface
  144. */
  145. private $serializer;
  146. /**
  147. * @param Layout\ProcessorFactory $processorFactory
  148. * @param ManagerInterface $eventManager
  149. * @param Layout\Data\Structure $structure
  150. * @param MessageManagerInterface $messageManager
  151. * @param Design\Theme\ResolverInterface $themeResolver
  152. * @param Layout\ReaderPool $readerPool
  153. * @param Layout\GeneratorPool $generatorPool
  154. * @param FrontendInterface $cache
  155. * @param Layout\Reader\ContextFactory $readerContextFactory
  156. * @param Layout\Generator\ContextFactory $generatorContextFactory
  157. * @param \Magento\Framework\App\State $appState
  158. * @param \Psr\Log\LoggerInterface $logger
  159. * @param bool $cacheable
  160. * @param SerializerInterface|null $serializer
  161. */
  162. public function __construct(
  163. Layout\ProcessorFactory $processorFactory,
  164. ManagerInterface $eventManager,
  165. Layout\Data\Structure $structure,
  166. MessageManagerInterface $messageManager,
  167. Design\Theme\ResolverInterface $themeResolver,
  168. Layout\ReaderPool $readerPool,
  169. Layout\GeneratorPool $generatorPool,
  170. FrontendInterface $cache,
  171. Layout\Reader\ContextFactory $readerContextFactory,
  172. Layout\Generator\ContextFactory $generatorContextFactory,
  173. AppState $appState,
  174. Logger $logger,
  175. $cacheable = true,
  176. SerializerInterface $serializer = null
  177. ) {
  178. $this->_elementClass = \Magento\Framework\View\Layout\Element::class;
  179. $this->_renderingOutput = new \Magento\Framework\DataObject();
  180. $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
  181. $this->_processorFactory = $processorFactory;
  182. $this->_eventManager = $eventManager;
  183. $this->structure = $structure;
  184. $this->messageManager = $messageManager;
  185. $this->themeResolver = $themeResolver;
  186. $this->readerPool = $readerPool;
  187. $this->generatorPool = $generatorPool;
  188. $this->cacheable = $cacheable;
  189. $this->cache = $cache;
  190. $this->readerContextFactory = $readerContextFactory;
  191. $this->generatorContextFactory = $generatorContextFactory;
  192. $this->appState = $appState;
  193. $this->logger = $logger;
  194. }
  195. /**
  196. * @param Layout\GeneratorPool $generatorPool
  197. * @return $this
  198. */
  199. public function setGeneratorPool(Layout\GeneratorPool $generatorPool)
  200. {
  201. $this->generatorPool = $generatorPool;
  202. return $this;
  203. }
  204. /**
  205. * @param Layout\BuilderInterface $builder
  206. * @return $this
  207. */
  208. public function setBuilder(Layout\BuilderInterface $builder)
  209. {
  210. $this->builder = $builder;
  211. return $this;
  212. }
  213. /**
  214. * Build layout blocks from generic layouts and/or page configurations
  215. *
  216. * @return void
  217. */
  218. protected function build()
  219. {
  220. if (!empty($this->builder)) {
  221. $this->builder->build();
  222. }
  223. }
  224. /**
  225. * TODO Will be eliminated in MAGETWO-28359
  226. * @return void
  227. */
  228. public function publicBuild()
  229. {
  230. $this->build();
  231. }
  232. /**
  233. * Cleanup circular references between layout & blocks
  234. *
  235. * Destructor should be called explicitly in order to work around the PHP bug
  236. * https://bugs.php.net/bug.php?id=62468
  237. */
  238. public function __destruct()
  239. {
  240. if (isset($this->_update) && is_object($this->_update)) {
  241. $this->_update->__destruct();
  242. $this->_update = null;
  243. }
  244. $this->_blocks = [];
  245. parent::__destruct();
  246. }
  247. /**
  248. * Retrieve the layout update instance
  249. *
  250. * @return \Magento\Framework\View\Layout\ProcessorInterface
  251. */
  252. public function getUpdate()
  253. {
  254. if (!$this->_update) {
  255. $theme = $this->themeResolver->get();
  256. $this->_update = $this->_processorFactory->create(['theme' => $theme]);
  257. }
  258. return $this->_update;
  259. }
  260. /**
  261. * Layout xml generation
  262. *
  263. * @return $this
  264. */
  265. public function generateXml()
  266. {
  267. $xml = $this->getUpdate()->asSimplexml();
  268. $this->setXml($xml);
  269. $this->structure->importElements([]);
  270. return $this;
  271. }
  272. /**
  273. * Create structure of elements from the loaded XML configuration
  274. *
  275. * @return void
  276. */
  277. public function generateElements()
  278. {
  279. \Magento\Framework\Profiler::start(__CLASS__ . '::' . __METHOD__);
  280. $cacheId = 'structure_' . $this->getUpdate()->getCacheId();
  281. $result = $this->cache->load($cacheId);
  282. if ($result) {
  283. $data = $this->serializer->unserialize($result);
  284. $this->getReaderContext()->getPageConfigStructure()->populateWithArray($data['pageConfigStructure']);
  285. $this->getReaderContext()->getScheduledStructure()->populateWithArray($data['scheduledStructure']);
  286. } else {
  287. \Magento\Framework\Profiler::start('build_structure');
  288. $this->readerPool->interpret($this->getReaderContext(), $this->getNode());
  289. \Magento\Framework\Profiler::stop('build_structure');
  290. $data = [
  291. 'pageConfigStructure' => $this->getReaderContext()->getPageConfigStructure()->__toArray(),
  292. 'scheduledStructure' => $this->getReaderContext()->getScheduledStructure()->__toArray(),
  293. ];
  294. $this->cache->save($this->serializer->serialize($data), $cacheId, $this->getUpdate()->getHandles());
  295. }
  296. $generatorContext = $this->generatorContextFactory->create(
  297. [
  298. 'structure' => $this->structure,
  299. 'layout' => $this,
  300. ]
  301. );
  302. \Magento\Framework\Profiler::start('generate_elements');
  303. $this->generatorPool->process($this->getReaderContext(), $generatorContext);
  304. \Magento\Framework\Profiler::stop('generate_elements');
  305. $this->addToOutputRootContainers();
  306. \Magento\Framework\Profiler::stop(__CLASS__ . '::' . __METHOD__);
  307. }
  308. /**
  309. * Add parent containers to output
  310. *
  311. * @return $this
  312. */
  313. protected function addToOutputRootContainers()
  314. {
  315. foreach ($this->structure->exportElements() as $name => $element) {
  316. if ($element['type'] === Element::TYPE_CONTAINER && empty($element['parent'])) {
  317. $this->addOutputElement($name);
  318. }
  319. }
  320. return $this;
  321. }
  322. /**
  323. * Get child block if exists
  324. *
  325. * @param string $parentName
  326. * @param string $alias
  327. * @return bool|\Magento\Framework\View\Element\AbstractBlock
  328. */
  329. public function getChildBlock($parentName, $alias)
  330. {
  331. $this->build();
  332. $name = $this->structure->getChildId($parentName, $alias);
  333. if ($this->isBlock($name)) {
  334. return $this->getBlock($name);
  335. }
  336. return false;
  337. }
  338. /**
  339. * Set child element into layout structure
  340. *
  341. * @param string $parentName
  342. * @param string $elementName
  343. * @param string $alias
  344. * @return $this
  345. */
  346. public function setChild($parentName, $elementName, $alias)
  347. {
  348. $this->build();
  349. $this->structure->setAsChild($elementName, $parentName, $alias);
  350. return $this;
  351. }
  352. /**
  353. * Reorder a child of a specified element
  354. *
  355. * If $offsetOrSibling is null, it will put the element to the end
  356. * If $offsetOrSibling is numeric (integer) value, it will put the element after/before specified position
  357. * Otherwise -- after/before specified sibling
  358. *
  359. * @param string $parentName
  360. * @param string $childName
  361. * @param string|int|null $offsetOrSibling
  362. * @param bool $after
  363. * @return void
  364. */
  365. public function reorderChild($parentName, $childName, $offsetOrSibling, $after = true)
  366. {
  367. $this->build();
  368. $this->structure->reorderChildElement($parentName, $childName, $offsetOrSibling, $after);
  369. }
  370. /**
  371. * Remove child element from parent
  372. *
  373. * @param string $parentName
  374. * @param string $alias
  375. * @return $this
  376. */
  377. public function unsetChild($parentName, $alias)
  378. {
  379. $this->build();
  380. $this->structure->unsetChild($parentName, $alias);
  381. return $this;
  382. }
  383. /**
  384. * Get list of child names
  385. *
  386. * @param string $parentName
  387. * @return array
  388. */
  389. public function getChildNames($parentName)
  390. {
  391. $this->build();
  392. return array_keys($this->structure->getChildren($parentName));
  393. }
  394. /**
  395. * Get list of child blocks
  396. *
  397. * Returns associative array of <alias> => <block instance>
  398. *
  399. * @param string $parentName
  400. * @return array
  401. */
  402. public function getChildBlocks($parentName)
  403. {
  404. $this->build();
  405. $blocks = [];
  406. foreach ($this->structure->getChildren($parentName) as $childName => $alias) {
  407. $block = $this->getBlock($childName);
  408. if ($block) {
  409. $blocks[$alias] = $block;
  410. }
  411. }
  412. return $blocks;
  413. }
  414. /**
  415. * Get child name by alias
  416. *
  417. * @param string $parentName
  418. * @param string $alias
  419. * @return bool|string
  420. */
  421. public function getChildName($parentName, $alias)
  422. {
  423. $this->build();
  424. return $this->structure->getChildId($parentName, $alias);
  425. }
  426. /**
  427. * Find an element in layout, render it and return string with its output
  428. *
  429. * @param string $name
  430. * @param bool $useCache
  431. * @return string
  432. */
  433. public function renderElement($name, $useCache = true)
  434. {
  435. $this->build();
  436. if (!isset($this->_renderElementCache[$name]) || !$useCache) {
  437. if ($this->displayElement($name)) {
  438. $this->_renderElementCache[$name] = $this->renderNonCachedElement($name);
  439. } else {
  440. return $this->_renderElementCache[$name] = '';
  441. }
  442. }
  443. $this->_renderingOutput->setData('output', $this->_renderElementCache[$name]);
  444. $this->_eventManager->dispatch(
  445. 'core_layout_render_element',
  446. ['element_name' => $name, 'layout' => $this, 'transport' => $this->_renderingOutput]
  447. );
  448. return $this->_renderingOutput->getData('output');
  449. }
  450. /**
  451. * Define whether to display element
  452. * Display if 'display' attribute is absent (false, null) or equal true ('1', true, 'true')
  453. * In any other cases - do not display
  454. *
  455. * @param string $name
  456. * @return bool
  457. */
  458. protected function displayElement($name)
  459. {
  460. $display = $this->structure->getAttribute($name, 'display');
  461. if ($display === '' || $display === false || $display === null
  462. || filter_var($display, FILTER_VALIDATE_BOOLEAN)) {
  463. return true;
  464. }
  465. return false;
  466. }
  467. /**
  468. * Render non cached element
  469. *
  470. * @param string $name
  471. * @return string
  472. * @throws \Exception
  473. */
  474. public function renderNonCachedElement($name)
  475. {
  476. $result = '';
  477. try {
  478. if ($this->isUiComponent($name)) {
  479. $result = $this->_renderUiComponent($name);
  480. } elseif ($this->isBlock($name)) {
  481. $result = $this->_renderBlock($name);
  482. } else {
  483. $result = $this->_renderContainer($name, false);
  484. }
  485. } catch (\Exception $e) {
  486. if ($this->appState->getMode() === AppState::MODE_DEVELOPER) {
  487. throw $e;
  488. }
  489. $message = ($e instanceof LocalizedException) ? $e->getLogMessage() : $e->getMessage();
  490. $this->logger->critical($message);
  491. }
  492. return $result;
  493. }
  494. /**
  495. * Gets HTML of block element
  496. *
  497. * @param string $name
  498. * @return string
  499. * @throws \Magento\Framework\Exception\LocalizedException
  500. */
  501. protected function _renderBlock($name)
  502. {
  503. $block = $this->getBlock($name);
  504. return $block ? $block->toHtml() : '';
  505. }
  506. /**
  507. * Gets HTML of Ui Component
  508. *
  509. * @param string $name
  510. * @return string
  511. * @throws \Magento\Framework\Exception\LocalizedException
  512. */
  513. protected function _renderUiComponent($name)
  514. {
  515. $uiComponent = $this->getUiComponent($name);
  516. return $uiComponent ? $uiComponent->toHtml() : '';
  517. }
  518. /**
  519. * Gets HTML of container element
  520. *
  521. * @param string $name
  522. * @param bool $useCache
  523. * @return string
  524. */
  525. protected function _renderContainer($name, $useCache = true)
  526. {
  527. $html = '';
  528. $children = $this->getChildNames($name);
  529. foreach ($children as $child) {
  530. $html .= $this->renderElement($child, $useCache);
  531. }
  532. if ($html == '' || !$this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_TAG)) {
  533. return $html;
  534. }
  535. $htmlId = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_ID);
  536. if ($htmlId) {
  537. $htmlId = ' id="' . $htmlId . '"';
  538. }
  539. $htmlClass = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_CLASS);
  540. if ($htmlClass) {
  541. $htmlClass = ' class="' . $htmlClass . '"';
  542. }
  543. $htmlTag = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_TAG);
  544. $html = sprintf('<%1$s%2$s%3$s>%4$s</%1$s>', $htmlTag, $htmlId, $htmlClass, $html);
  545. return $html;
  546. }
  547. /**
  548. * Add element to parent group
  549. *
  550. * @param string $blockName
  551. * @param string $parentGroupName
  552. * @return bool
  553. */
  554. public function addToParentGroup($blockName, $parentGroupName)
  555. {
  556. $this->build();
  557. return $this->structure->addToParentGroup($blockName, $parentGroupName);
  558. }
  559. /**
  560. * Get element names for specified group
  561. *
  562. * @param string $blockName
  563. * @param string $groupName
  564. * @return array
  565. */
  566. public function getGroupChildNames($blockName, $groupName)
  567. {
  568. $this->build();
  569. return $this->structure->getGroupChildNames($blockName, $groupName);
  570. }
  571. /**
  572. * Check if element exists in layout structure
  573. *
  574. * @param string $name
  575. * @return bool
  576. */
  577. public function hasElement($name)
  578. {
  579. $this->build();
  580. return $this->structure->hasElement($name);
  581. }
  582. /**
  583. * Get property value of an element
  584. *
  585. * @param string $name
  586. * @param string $attribute
  587. * @return mixed
  588. */
  589. public function getElementProperty($name, $attribute)
  590. {
  591. $this->build();
  592. return $this->structure->getAttribute($name, $attribute);
  593. }
  594. /**
  595. * Whether specified element is a block
  596. *
  597. * @param string $name
  598. * @return bool
  599. */
  600. public function isBlock($name)
  601. {
  602. $this->build();
  603. if ($this->structure->hasElement($name)) {
  604. return Element::TYPE_BLOCK === $this->structure->getAttribute($name, 'type');
  605. }
  606. return false;
  607. }
  608. /**
  609. * Whether specified element is a UI Component
  610. *
  611. * @param string $name
  612. * @return bool
  613. */
  614. public function isUiComponent($name)
  615. {
  616. $this->build();
  617. if ($this->structure->hasElement($name)) {
  618. return Element::TYPE_UI_COMPONENT === $this->structure->getAttribute($name, 'type');
  619. }
  620. return false;
  621. }
  622. /**
  623. * Checks if element with specified name is container
  624. *
  625. * @param string $name
  626. * @return bool
  627. */
  628. public function isContainer($name)
  629. {
  630. $this->build();
  631. if ($this->structure->hasElement($name)) {
  632. return Element::TYPE_CONTAINER === $this->structure->getAttribute($name, 'type');
  633. }
  634. return false;
  635. }
  636. /**
  637. * Whether the specified element may be manipulated externally
  638. *
  639. * @param string $name
  640. * @return bool
  641. */
  642. public function isManipulationAllowed($name)
  643. {
  644. $this->build();
  645. $parentName = $this->structure->getParentId($name);
  646. return $parentName && $this->isContainer($parentName);
  647. }
  648. /**
  649. * Save block in blocks registry
  650. *
  651. * @param string $name
  652. * @param \Magento\Framework\View\Element\AbstractBlock $block
  653. * @return $this
  654. */
  655. public function setBlock($name, $block)
  656. {
  657. $this->_blocks[$name] = $block;
  658. return $this;
  659. }
  660. /**
  661. * Remove block from registry
  662. *
  663. * @param string $name
  664. * @return $this
  665. */
  666. public function unsetElement($name)
  667. {
  668. $this->build();
  669. if (isset($this->_blocks[$name])) {
  670. $this->_blocks[$name] = null;
  671. unset($this->_blocks[$name]);
  672. }
  673. $this->structure->unsetElement($name);
  674. return $this;
  675. }
  676. /**
  677. * Block Factory
  678. *
  679. * @param string $type
  680. * @param string $name
  681. * @param array $arguments
  682. * @return \Magento\Framework\View\Element\AbstractBlock
  683. */
  684. public function createBlock($type, $name = '', array $arguments = [])
  685. {
  686. $this->build();
  687. $name = $this->structure->createStructuralElement($name, Element::TYPE_BLOCK, $type);
  688. $block = $this->_createBlock($type, $name, $arguments);
  689. $block->setLayout($this);
  690. return $block;
  691. }
  692. /**
  693. * Create block and add to layout
  694. *
  695. * @param string $type
  696. * @param string $name
  697. * @param array $arguments
  698. * @return \Magento\Framework\View\Element\AbstractBlock
  699. */
  700. protected function _createBlock($type, $name, array $arguments = [])
  701. {
  702. /** @var \Magento\Framework\View\Layout\Generator\Block $blockGenerator */
  703. $blockGenerator = $this->generatorPool->getGenerator(Layout\Generator\Block::TYPE);
  704. $block = $blockGenerator->createBlock($type, $name, $arguments);
  705. $this->setBlock($name, $block);
  706. return $block;
  707. }
  708. /**
  709. * Add a block to registry, create new object if needed
  710. *
  711. * @param string|\Magento\Framework\View\Element\AbstractBlock $block
  712. * @param string $name
  713. * @param string $parent
  714. * @param string $alias
  715. * @return \Magento\Framework\View\Element\AbstractBlock
  716. */
  717. public function addBlock($block, $name = '', $parent = '', $alias = '')
  718. {
  719. $this->build();
  720. if ($block instanceof \Magento\Framework\View\Element\AbstractBlock) {
  721. $name = $name ?: $block->getNameInLayout();
  722. } else {
  723. $block = $this->_createBlock($block, $name);
  724. }
  725. $name = $this->structure->createStructuralElement(
  726. $name,
  727. Element::TYPE_BLOCK,
  728. $name ?: get_class($block)
  729. );
  730. $this->setBlock($name, $block);
  731. $block->setNameInLayout($name);
  732. if ($parent) {
  733. $this->structure->setAsChild($name, $parent, $alias);
  734. }
  735. $block->setLayout($this);
  736. return $block;
  737. }
  738. /**
  739. * Insert container into layout structure
  740. *
  741. * @param string $name
  742. * @param string $label
  743. * @param array $options
  744. * @param string $parent
  745. * @param string $alias
  746. * @return void
  747. */
  748. public function addContainer($name, $label, array $options = [], $parent = '', $alias = '')
  749. {
  750. $this->build();
  751. $name = $this->structure->createStructuralElement($name, Element::TYPE_CONTAINER, $alias);
  752. $options[Layout\Element::CONTAINER_OPT_LABEL] = $label;
  753. /** @var \Magento\Framework\View\Layout\Generator\Container $containerGenerator */
  754. $containerGenerator = $this->generatorPool->getGenerator(Layout\Generator\Container::TYPE);
  755. $containerGenerator->generateContainer($this->structure, $name, $options);
  756. if ($parent) {
  757. $this->structure->setAsChild($name, $parent, $alias);
  758. }
  759. }
  760. /**
  761. * Rename element in layout and layout structure
  762. *
  763. * @param string $oldName
  764. * @param string $newName
  765. * @return bool
  766. */
  767. public function renameElement($oldName, $newName)
  768. {
  769. $this->build();
  770. if (isset($this->_blocks[$oldName])) {
  771. $block = $this->_blocks[$oldName];
  772. $this->_blocks[$oldName] = null;
  773. unset($this->_blocks[$oldName]);
  774. $this->_blocks[$newName] = $block;
  775. }
  776. $this->structure->renameElement($oldName, $newName);
  777. return $this;
  778. }
  779. /**
  780. * Retrieve all blocks from registry as array
  781. *
  782. * @return array
  783. */
  784. public function getAllBlocks()
  785. {
  786. $this->build();
  787. return $this->_blocks;
  788. }
  789. /**
  790. * Get block object by name
  791. *
  792. * @param string $name
  793. * @return \Magento\Framework\View\Element\AbstractBlock|bool
  794. */
  795. public function getBlock($name)
  796. {
  797. $this->build();
  798. if (isset($this->_blocks[$name])) {
  799. return $this->_blocks[$name];
  800. } else {
  801. return false;
  802. }
  803. }
  804. /**
  805. * Get Ui Component object by name
  806. *
  807. * @param string $name
  808. * @return \Magento\Framework\View\Element\AbstractBlock|bool
  809. */
  810. public function getUiComponent($name)
  811. {
  812. return $this->getBlock($name);
  813. }
  814. /**
  815. * Gets parent name of an element with specified name
  816. *
  817. * @param string $childName
  818. * @return bool|string
  819. */
  820. public function getParentName($childName)
  821. {
  822. $this->build();
  823. return $this->structure->getParentId($childName);
  824. }
  825. /**
  826. * Get element alias by name
  827. *
  828. * @param string $name
  829. * @return bool|string
  830. */
  831. public function getElementAlias($name)
  832. {
  833. $this->build();
  834. return $this->structure->getChildAlias($this->structure->getParentId($name), $name);
  835. }
  836. /**
  837. * Add an element to output
  838. *
  839. * @param string $name
  840. * @return $this
  841. */
  842. public function addOutputElement($name)
  843. {
  844. $this->_output[$name] = $name;
  845. return $this;
  846. }
  847. /**
  848. * Remove an element from output
  849. *
  850. * @param string $name
  851. * @return $this
  852. */
  853. public function removeOutputElement($name)
  854. {
  855. if (isset($this->_output[$name])) {
  856. unset($this->_output[$name]);
  857. }
  858. return $this;
  859. }
  860. /**
  861. * Get all blocks marked for output
  862. *
  863. * @return string
  864. */
  865. public function getOutput()
  866. {
  867. $this->build();
  868. $out = '';
  869. foreach ($this->_output as $name) {
  870. $out .= $this->renderElement($name);
  871. }
  872. return $out;
  873. }
  874. /**
  875. * Retrieve messages block
  876. *
  877. * @return \Magento\Framework\View\Element\Messages
  878. */
  879. public function getMessagesBlock()
  880. {
  881. $this->build();
  882. $block = $this->getBlock('messages');
  883. if ($block) {
  884. return $block;
  885. }
  886. return $this->createBlock(\Magento\Framework\View\Element\Messages::class, 'messages');
  887. }
  888. /**
  889. * Get block singleton
  890. *
  891. * @param string $type
  892. * @return \Magento\Framework\App\Helper\AbstractHelper
  893. * @throws \Magento\Framework\Exception\LocalizedException
  894. */
  895. public function getBlockSingleton($type)
  896. {
  897. if (empty($type)) {
  898. throw new \Magento\Framework\Exception\LocalizedException(
  899. new \Magento\Framework\Phrase('Invalid block type')
  900. );
  901. }
  902. if (!isset($this->sharedBlocks[$type])) {
  903. $this->sharedBlocks[$type] = $this->createBlock($type);
  904. }
  905. return $this->sharedBlocks[$type];
  906. }
  907. /**
  908. * @param string $namespace
  909. * @param string $staticType
  910. * @param string $dynamicType
  911. * @param string $type
  912. * @param string $template
  913. * @param array $data
  914. * @return $this
  915. */
  916. public function addAdjustableRenderer($namespace, $staticType, $dynamicType, $type, $template, $data = [])
  917. {
  918. $this->_renderers[$namespace][$staticType][$dynamicType] = [
  919. 'type' => $type,
  920. 'template' => $template,
  921. 'data' => $data,
  922. ];
  923. return $this;
  924. }
  925. /**
  926. * @param string $namespace
  927. * @param string $staticType
  928. * @param string $dynamicType
  929. * @return array|null
  930. */
  931. public function getRendererOptions($namespace, $staticType, $dynamicType)
  932. {
  933. if (!isset($this->_renderers[$namespace])) {
  934. return null;
  935. }
  936. if (!isset($this->_renderers[$namespace][$staticType])) {
  937. return null;
  938. }
  939. if (!isset($this->_renderers[$namespace][$staticType][$dynamicType])) {
  940. return null;
  941. }
  942. return $this->_renderers[$namespace][$staticType][$dynamicType];
  943. }
  944. /**
  945. * @param string $namespace
  946. * @param string $staticType
  947. * @param string $dynamicType
  948. * @param array $data
  949. * @return void
  950. */
  951. public function executeRenderer($namespace, $staticType, $dynamicType, $data = [])
  952. {
  953. $this->build();
  954. if ($options = $this->getRendererOptions($namespace, $staticType, $dynamicType)) {
  955. $dictionary = [];
  956. /** @var $block \Magento\Framework\View\Element\Template */
  957. $block = $this->createBlock($options['type'], '')
  958. ->setData($data)
  959. ->assign($dictionary)
  960. ->setTemplate($options['template'])
  961. ->assign($data);
  962. echo $this->_renderBlock($block->getNameInLayout());
  963. }
  964. }
  965. /**
  966. * Init messages by message storage(s), loading and adding messages to layout messages block
  967. *
  968. * @param string|array $messageGroups
  969. * @return void
  970. * @throws \UnexpectedValueException
  971. */
  972. public function initMessages($messageGroups = [])
  973. {
  974. $this->build();
  975. foreach ($this->_prepareMessageGroup($messageGroups) as $group) {
  976. $block = $this->getMessagesBlock();
  977. $block->addMessages($this->messageManager->getMessages(true, $group));
  978. $block->addStorageType($group);
  979. }
  980. }
  981. /**
  982. * Validate message groups
  983. *
  984. * @param array $messageGroups
  985. * @return array
  986. */
  987. protected function _prepareMessageGroup($messageGroups)
  988. {
  989. if (!is_array($messageGroups)) {
  990. $messageGroups = [$messageGroups];
  991. } elseif (empty($messageGroups)) {
  992. $messageGroups[] = $this->messageManager->getDefaultGroup();
  993. }
  994. return $messageGroups;
  995. }
  996. /**
  997. * Check is exists non-cacheable layout elements
  998. *
  999. * @return bool
  1000. */
  1001. public function isCacheable()
  1002. {
  1003. $this->build();
  1004. $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
  1005. return $this->cacheable && $cacheableXml;
  1006. }
  1007. /**
  1008. * Check is exists non-cacheable layout elements
  1009. *
  1010. * @return bool
  1011. */
  1012. public function isPrivate()
  1013. {
  1014. return $this->isPrivate;
  1015. }
  1016. /**
  1017. * Mark layout as private
  1018. *
  1019. * @param bool $isPrivate
  1020. * @return Layout
  1021. */
  1022. public function setIsPrivate($isPrivate = true)
  1023. {
  1024. $this->isPrivate = (bool)$isPrivate;
  1025. return $this;
  1026. }
  1027. /**
  1028. * Getter and lazy loader for xml element
  1029. *
  1030. * @return \Magento\Framework\Simplexml\Element
  1031. */
  1032. protected function getXml()
  1033. {
  1034. if (!$this->_xml) {
  1035. $this->setXml(simplexml_load_string(self::LAYOUT_NODE, $this->_elementClass));
  1036. }
  1037. return $this->_xml;
  1038. }
  1039. /**
  1040. * Getter and lazy loader for reader context
  1041. *
  1042. * @return Layout\Reader\Context
  1043. */
  1044. public function getReaderContext()
  1045. {
  1046. if (!$this->readerContext) {
  1047. $this->readerContext = $this->readerContextFactory->create();
  1048. }
  1049. return $this->readerContext;
  1050. }
  1051. }