Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Layout.php
Go to the documentation of this file.
1 <?php
6 namespace Magento\Framework\View;
7 
13 use Magento\Framework\Message\ManagerInterface as MessageManagerInterface;
16 use Psr\Log\LoggerInterface as Logger;
17 
29 {
30 
34  const LAYOUT_NODE = '<layout/>';
35 
41  protected $_update;
42 
48  protected $_blocks = [];
49 
55  protected $_output = [];
56 
62  protected $sharedBlocks = [];
63 
69  protected $_renderingOutput;
70 
76  protected $_renderElementCache = [];
77 
83  protected $structure;
84 
90  protected $_renderers = [];
91 
97  protected $_eventManager;
98 
103 
107  protected $messageManager;
108 
112  protected $isPrivate = false;
113 
117  protected $themeResolver;
118 
122  protected $readerPool;
123 
127  protected $cacheable;
128 
132  protected $generatorPool;
133 
137  protected $builder;
138 
142  protected $cache;
143 
148 
153 
157  protected $readerContext;
158 
162  protected $appState;
163 
167  protected $logger;
168 
172  private $serializer;
173 
190  public function __construct(
191  Layout\ProcessorFactory $processorFactory,
192  ManagerInterface $eventManager,
193  Layout\Data\Structure $structure,
194  MessageManagerInterface $messageManager,
195  Design\Theme\ResolverInterface $themeResolver,
196  Layout\ReaderPool $readerPool,
197  Layout\GeneratorPool $generatorPool,
199  Layout\Reader\ContextFactory $readerContextFactory,
200  Layout\Generator\ContextFactory $generatorContextFactory,
202  Logger $logger,
203  $cacheable = true,
204  SerializerInterface $serializer = null
205  ) {
206  $this->_elementClass = \Magento\Framework\View\Layout\Element::class;
207  $this->_renderingOutput = new \Magento\Framework\DataObject();
208  $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);
209 
210  $this->_processorFactory = $processorFactory;
211  $this->_eventManager = $eventManager;
212  $this->structure = $structure;
213  $this->messageManager = $messageManager;
214  $this->themeResolver = $themeResolver;
215  $this->readerPool = $readerPool;
216  $this->generatorPool = $generatorPool;
217  $this->cacheable = $cacheable;
218  $this->cache = $cache;
219  $this->readerContextFactory = $readerContextFactory;
220  $this->generatorContextFactory = $generatorContextFactory;
221  $this->appState = $appState;
222  $this->logger = $logger;
223  }
224 
229  public function setGeneratorPool(Layout\GeneratorPool $generatorPool)
230  {
231  $this->generatorPool = $generatorPool;
232  return $this;
233  }
234 
239  public function setBuilder(Layout\BuilderInterface $builder)
240  {
241  $this->builder = $builder;
242  return $this;
243  }
244 
250  protected function build()
251  {
252  if (!empty($this->builder)) {
253  $this->builder->build();
254  }
255  }
256 
261  public function publicBuild()
262  {
263  $this->build();
264  }
265 
272  public function __destruct()
273  {
274  if (isset($this->_update) && is_object($this->_update)) {
275  $this->_update->__destruct();
276  $this->_update = null;
277  }
278  $this->_blocks = [];
279  parent::__destruct();
280  }
281 
287  public function getUpdate()
288  {
289  if (!$this->_update) {
290  $theme = $this->themeResolver->get();
291  $this->_update = $this->_processorFactory->create(['theme' => $theme]);
292  }
293  return $this->_update;
294  }
295 
301  public function generateXml()
302  {
303  $xml = $this->getUpdate()->asSimplexml();
304  $this->setXml($xml);
305  $this->structure->importElements([]);
306  return $this;
307  }
308 
314  public function generateElements()
315  {
316  \Magento\Framework\Profiler::start(__CLASS__ . '::' . __METHOD__);
317  $cacheId = 'structure_' . $this->getUpdate()->getCacheId();
318  $result = $this->cache->load($cacheId);
319  if ($result) {
320  $data = $this->serializer->unserialize($result);
321  $this->getReaderContext()->getPageConfigStructure()->populateWithArray($data['pageConfigStructure']);
322  $this->getReaderContext()->getScheduledStructure()->populateWithArray($data['scheduledStructure']);
323  } else {
324  \Magento\Framework\Profiler::start('build_structure');
325  $this->readerPool->interpret($this->getReaderContext(), $this->getNode());
326  \Magento\Framework\Profiler::stop('build_structure');
327 
328  $data = [
329  'pageConfigStructure' => $this->getReaderContext()->getPageConfigStructure()->__toArray(),
330  'scheduledStructure' => $this->getReaderContext()->getScheduledStructure()->__toArray(),
331  ];
332  $this->cache->save($this->serializer->serialize($data), $cacheId, $this->getUpdate()->getHandles());
333  }
334 
335  $generatorContext = $this->generatorContextFactory->create(
336  [
337  'structure' => $this->structure,
338  'layout' => $this,
339  ]
340  );
341 
342  \Magento\Framework\Profiler::start('generate_elements');
343  $this->generatorPool->process($this->getReaderContext(), $generatorContext);
344  \Magento\Framework\Profiler::stop('generate_elements');
345 
346  $this->addToOutputRootContainers();
347  \Magento\Framework\Profiler::stop(__CLASS__ . '::' . __METHOD__);
348  }
349 
355  protected function addToOutputRootContainers()
356  {
357  foreach ($this->structure->exportElements() as $name => $element) {
358  if ($element['type'] === Element::TYPE_CONTAINER && empty($element['parent'])) {
359  $this->addOutputElement($name);
360  }
361  }
362  return $this;
363  }
364 
372  public function getChildBlock($parentName, $alias)
373  {
374  $this->build();
375  $name = $this->structure->getChildId($parentName, $alias);
376  if ($this->isBlock($name)) {
377  return $this->getBlock($name);
378  }
379  return false;
380  }
381 
390  public function setChild($parentName, $elementName, $alias)
391  {
392  $this->build();
393  $this->structure->setAsChild($elementName, $parentName, $alias);
394  return $this;
395  }
396 
410  public function reorderChild($parentName, $childName, $offsetOrSibling, $after = true)
411  {
412  $this->build();
413  $this->structure->reorderChildElement($parentName, $childName, $offsetOrSibling, $after);
414  }
415 
423  public function unsetChild($parentName, $alias)
424  {
425  $this->build();
426  $this->structure->unsetChild($parentName, $alias);
427  return $this;
428  }
429 
436  public function getChildNames($parentName)
437  {
438  $this->build();
439  return array_keys($this->structure->getChildren($parentName));
440  }
441 
450  public function getChildBlocks($parentName)
451  {
452  $this->build();
453  $blocks = [];
454  foreach ($this->structure->getChildren($parentName) as $childName => $alias) {
455  $block = $this->getBlock($childName);
456  if ($block) {
457  $blocks[$alias] = $block;
458  }
459  }
460  return $blocks;
461  }
462 
470  public function getChildName($parentName, $alias)
471  {
472  $this->build();
473  return $this->structure->getChildId($parentName, $alias);
474  }
475 
483  public function renderElement($name, $useCache = true)
484  {
485  $this->build();
486  if (!isset($this->_renderElementCache[$name]) || !$useCache) {
487  if ($this->displayElement($name)) {
488  $this->_renderElementCache[$name] = $this->renderNonCachedElement($name);
489  } else {
490  return $this->_renderElementCache[$name] = '';
491  }
492  }
493  $this->_renderingOutput->setData('output', $this->_renderElementCache[$name]);
494  $this->_eventManager->dispatch(
495  'core_layout_render_element',
496  ['element_name' => $name, 'layout' => $this, 'transport' => $this->_renderingOutput]
497  );
498  return $this->_renderingOutput->getData('output');
499  }
500 
509  protected function displayElement($name)
510  {
511  $display = $this->structure->getAttribute($name, 'display');
512  if ($display === '' || $display === false || $display === null
513  || filter_var($display, FILTER_VALIDATE_BOOLEAN)) {
514  return true;
515  }
516  return false;
517  }
518 
526  public function renderNonCachedElement($name)
527  {
528  $result = '';
529  try {
530  if ($this->isUiComponent($name)) {
531  $result = $this->_renderUiComponent($name);
532  } elseif ($this->isBlock($name)) {
533  $result = $this->_renderBlock($name);
534  } else {
535  $result = $this->_renderContainer($name, false);
536  }
537  } catch (\Exception $e) {
538  if ($this->appState->getMode() === AppState::MODE_DEVELOPER) {
539  throw $e;
540  }
541  $message = ($e instanceof LocalizedException) ? $e->getLogMessage() : $e->getMessage();
542  $this->logger->critical($message);
543  }
544  return $result;
545  }
546 
554  protected function _renderBlock($name)
555  {
556  $block = $this->getBlock($name);
557  return $block ? $block->toHtml() : '';
558  }
559 
567  protected function _renderUiComponent($name)
568  {
569  $uiComponent = $this->getUiComponent($name);
570  return $uiComponent ? $uiComponent->toHtml() : '';
571  }
572 
580  protected function _renderContainer($name, $useCache = true)
581  {
582  $html = '';
583  $children = $this->getChildNames($name);
584  foreach ($children as $child) {
585  $html .= $this->renderElement($child, $useCache);
586  }
587  if ($html == '' || !$this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_TAG)) {
588  return $html;
589  }
590 
591  $htmlId = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_ID);
592  if ($htmlId) {
593  $htmlId = ' id="' . $htmlId . '"';
594  }
595 
596  $htmlClass = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_CLASS);
597  if ($htmlClass) {
598  $htmlClass = ' class="' . $htmlClass . '"';
599  }
600 
601  $htmlTag = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_TAG);
602 
603  $html = sprintf('<%1$s%2$s%3$s>%4$s</%1$s>', $htmlTag, $htmlId, $htmlClass, $html);
604 
605  return $html;
606  }
607 
615  public function addToParentGroup($blockName, $parentGroupName)
616  {
617  $this->build();
618  return $this->structure->addToParentGroup($blockName, $parentGroupName);
619  }
620 
628  public function getGroupChildNames($blockName, $groupName)
629  {
630  $this->build();
631  return $this->structure->getGroupChildNames($blockName, $groupName);
632  }
633 
640  public function hasElement($name)
641  {
642  $this->build();
643  return $this->structure->hasElement($name);
644  }
645 
654  {
655  $this->build();
656  return $this->structure->getAttribute($name, $attribute);
657  }
658 
665  public function isBlock($name)
666  {
667  $this->build();
668  if ($this->structure->hasElement($name)) {
669  return Element::TYPE_BLOCK === $this->structure->getAttribute($name, 'type');
670  }
671  return false;
672  }
673 
680  public function isUiComponent($name)
681  {
682  $this->build();
683  if ($this->structure->hasElement($name)) {
684  return Element::TYPE_UI_COMPONENT === $this->structure->getAttribute($name, 'type');
685  }
686  return false;
687  }
688 
695  public function isContainer($name)
696  {
697  $this->build();
698  if ($this->structure->hasElement($name)) {
699  return Element::TYPE_CONTAINER === $this->structure->getAttribute($name, 'type');
700  }
701  return false;
702  }
703 
710  public function isManipulationAllowed($name)
711  {
712  $this->build();
713  $parentName = $this->structure->getParentId($name);
714  return $parentName && $this->isContainer($parentName);
715  }
716 
724  public function setBlock($name, $block)
725  {
726  $this->_blocks[$name] = $block;
727  return $this;
728  }
729 
736  public function unsetElement($name)
737  {
738  $this->build();
739  if (isset($this->_blocks[$name])) {
740  $this->_blocks[$name] = null;
741  unset($this->_blocks[$name]);
742  }
743  $this->structure->unsetElement($name);
744  return $this;
745  }
746 
755  public function createBlock($type, $name = '', array $arguments = [])
756  {
757  $this->build();
758  $name = $this->structure->createStructuralElement($name, Element::TYPE_BLOCK, $type);
759  $block = $this->_createBlock($type, $name, $arguments);
760  $block->setLayout($this);
761  return $block;
762  }
763 
772  protected function _createBlock($type, $name, array $arguments = [])
773  {
775  $blockGenerator = $this->generatorPool->getGenerator(Layout\Generator\Block::TYPE);
776  $block = $blockGenerator->createBlock($type, $name, $arguments);
777  $this->setBlock($name, $block);
778  return $block;
779  }
780 
790  public function addBlock($block, $name = '', $parent = '', $alias = '')
791  {
792  $this->build();
793  if ($block instanceof \Magento\Framework\View\Element\AbstractBlock) {
794  $name = $name ?: $block->getNameInLayout();
795  } else {
796  $block = $this->_createBlock($block, $name);
797  }
798  $name = $this->structure->createStructuralElement(
799  $name,
801  $name ?: get_class($block)
802  );
803  $this->setBlock($name, $block);
804  $block->setNameInLayout($name);
805  if ($parent) {
806  $this->structure->setAsChild($name, $parent, $alias);
807  }
808  $block->setLayout($this);
809  return $block;
810  }
811 
822  public function addContainer($name, $label, array $options = [], $parent = '', $alias = '')
823  {
824  $this->build();
825  $name = $this->structure->createStructuralElement($name, Element::TYPE_CONTAINER, $alias);
828  $containerGenerator = $this->generatorPool->getGenerator(Layout\Generator\Container::TYPE);
829  $containerGenerator->generateContainer($this->structure, $name, $options);
830  if ($parent) {
831  $this->structure->setAsChild($name, $parent, $alias);
832  }
833  }
834 
842  public function renameElement($oldName, $newName)
843  {
844  $this->build();
845  if (isset($this->_blocks[$oldName])) {
846  $block = $this->_blocks[$oldName];
847  $this->_blocks[$oldName] = null;
848  unset($this->_blocks[$oldName]);
849  $this->_blocks[$newName] = $block;
850  }
851  $this->structure->renameElement($oldName, $newName);
852 
853  return $this;
854  }
855 
861  public function getAllBlocks()
862  {
863  $this->build();
864  return $this->_blocks;
865  }
866 
873  public function getBlock($name)
874  {
875  $this->build();
876  if (isset($this->_blocks[$name])) {
877  return $this->_blocks[$name];
878  } else {
879  return false;
880  }
881  }
882 
889  public function getUiComponent($name)
890  {
891  return $this->getBlock($name);
892  }
893 
900  public function getParentName($childName)
901  {
902  $this->build();
903  return $this->structure->getParentId($childName);
904  }
905 
912  public function getElementAlias($name)
913  {
914  $this->build();
915  return $this->structure->getChildAlias($this->structure->getParentId($name), $name);
916  }
917 
924  public function addOutputElement($name)
925  {
926  $this->_output[$name] = $name;
927  return $this;
928  }
929 
936  public function removeOutputElement($name)
937  {
938  if (isset($this->_output[$name])) {
939  unset($this->_output[$name]);
940  }
941  return $this;
942  }
943 
949  public function getOutput()
950  {
951  $this->build();
952  $out = '';
953  foreach ($this->_output as $name) {
954  $out .= $this->renderElement($name);
955  }
956  return $out;
957  }
958 
964  public function getMessagesBlock()
965  {
966  $this->build();
967  $block = $this->getBlock('messages');
968  if ($block) {
969  return $block;
970  }
971  return $this->createBlock(\Magento\Framework\View\Element\Messages::class, 'messages');
972  }
973 
981  public function getBlockSingleton($type)
982  {
983  if (empty($type)) {
984  throw new \Magento\Framework\Exception\LocalizedException(
985  new \Magento\Framework\Phrase('Invalid block type')
986  );
987  }
988  if (!isset($this->sharedBlocks[$type])) {
989  $this->sharedBlocks[$type] = $this->createBlock($type);
990  }
991  return $this->sharedBlocks[$type];
992  }
993 
1003  public function addAdjustableRenderer($namespace, $staticType, $dynamicType, $type, $template, $data = [])
1004  {
1005  $this->_renderers[$namespace][$staticType][$dynamicType] = [
1006  'type' => $type,
1007  'template' => $template,
1008  'data' => $data,
1009  ];
1010  return $this;
1011  }
1012 
1019  public function getRendererOptions($namespace, $staticType, $dynamicType)
1020  {
1021  if (!isset($this->_renderers[$namespace])) {
1022  return null;
1023  }
1024  if (!isset($this->_renderers[$namespace][$staticType])) {
1025  return null;
1026  }
1027  if (!isset($this->_renderers[$namespace][$staticType][$dynamicType])) {
1028  return null;
1029  }
1030  return $this->_renderers[$namespace][$staticType][$dynamicType];
1031  }
1032 
1040  public function executeRenderer($namespace, $staticType, $dynamicType, $data = [])
1041  {
1042  $this->build();
1043  if ($options = $this->getRendererOptions($namespace, $staticType, $dynamicType)) {
1044  $dictionary = [];
1046  $block = $this->createBlock($options['type'], '')
1047  ->setData($data)
1048  ->assign($dictionary)
1049  ->setTemplate($options['template'])
1050  ->assign($data);
1051 
1052  echo $this->_renderBlock($block->getNameInLayout());
1053  }
1054  }
1055 
1063  public function initMessages($messageGroups = [])
1064  {
1065  $this->build();
1066  foreach ($this->_prepareMessageGroup($messageGroups) as $group) {
1067  $block = $this->getMessagesBlock();
1068  $block->addMessages($this->messageManager->getMessages(true, $group));
1069  $block->addStorageType($group);
1070  }
1071  }
1072 
1079  protected function _prepareMessageGroup($messageGroups)
1080  {
1081  if (!is_array($messageGroups)) {
1082  $messageGroups = [$messageGroups];
1083  } elseif (empty($messageGroups)) {
1084  $messageGroups[] = $this->messageManager->getDefaultGroup();
1085  }
1086  return $messageGroups;
1087  }
1088 
1094  public function isCacheable()
1095  {
1096  $this->build();
1097  $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
1098  return $this->cacheable && $cacheableXml;
1099  }
1100 
1106  public function isPrivate()
1107  {
1108  return $this->isPrivate;
1109  }
1110 
1117  public function setIsPrivate($isPrivate = true)
1118  {
1119  $this->isPrivate = (bool)$isPrivate;
1120  return $this;
1121  }
1122 
1128  protected function getXml()
1129  {
1130  if (!$this->_xml) {
1131  $this->setXml(simplexml_load_string(self::LAYOUT_NODE, $this->_elementClass));
1132  }
1133  return $this->_xml;
1134  }
1135 
1141  public function getReaderContext()
1142  {
1143  if (!$this->readerContext) {
1144  $this->readerContext = $this->readerContextFactory->create();
1145  }
1146  return $this->readerContext;
1147  }
1148 }
getChildName($parentName, $alias)
Definition: Layout.php:470
setBuilder(Layout\BuilderInterface $builder)
Definition: Layout.php:239
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
unsetChild($parentName, $alias)
Definition: Layout.php:423
getRendererOptions($namespace, $staticType, $dynamicType)
Definition: Layout.php:1019
$processorFactory
Definition: 404.php:9
renameElement($oldName, $newName)
Definition: Layout.php:842
_renderContainer($name, $useCache=true)
Definition: Layout.php:580
getChildBlock($parentName, $alias)
Definition: Layout.php:372
$group
Definition: sections.phtml:16
setBlock($name, $block)
Definition: Layout.php:724
initMessages($messageGroups=[])
Definition: Layout.php:1063
$message
renderElement($name, $useCache=true)
Definition: Layout.php:483
addContainer($name, $label, array $options=[], $parent='', $alias='')
getChildNames($parentName)
Definition: Layout.php:436
$block
Definition: block.php:8
addAdjustableRenderer($namespace, $staticType, $dynamicType, $type, $template, $data=[])
Definition: Layout.php:1003
$type
Definition: item.phtml:13
getGroupChildNames($blockName, $groupName)
Definition: Layout.php:628
addToParentGroup($blockName, $parentGroupName)
Definition: Layout.php:615
$label
Definition: details.phtml:21
createBlock($type, $name='', array $arguments=[])
Definition: Layout.php:755
getElementProperty($name, $attribute)
Definition: Layout.php:653
$theme
$arguments
setChild($parentName, $elementName, $alias)
Definition: Layout.php:390
reorderChild($parentName, $childName, $offsetOrSibling, $after=true)
Definition: Layout.php:410
$children
Definition: actions.phtml:11
addBlock($block, $name='', $parent='', $alias='')
Definition: Layout.php:790
_prepareMessageGroup($messageGroups)
Definition: Layout.php:1079
if(!trim($html)) $alias
Definition: details.phtml:20
getChildBlocks($parentName)
Definition: Layout.php:450
__construct(Layout\ProcessorFactory $processorFactory, ManagerInterface $eventManager, Layout\Data\Structure $structure, MessageManagerInterface $messageManager, Design\Theme\ResolverInterface $themeResolver, Layout\ReaderPool $readerPool, Layout\GeneratorPool $generatorPool, FrontendInterface $cache, Layout\Reader\ContextFactory $readerContextFactory, Layout\Generator\ContextFactory $generatorContextFactory, AppState $appState, Logger $logger, $cacheable=true, SerializerInterface $serializer=null)
Definition: Layout.php:190
setGeneratorPool(Layout\GeneratorPool $generatorPool)
Definition: Layout.php:229
setIsPrivate($isPrivate=true)
Definition: Layout.php:1117
$template
Definition: export.php:12
$elementName
Definition: gallery.phtml:10
if(!isset($_GET['name'])) $name
Definition: log.php:14
$element
Definition: element.phtml:12