/** * {@inheritdoc} */ public function searchAndRenderBlock(FormView $view, $blockNameSuffix, array $variables = array()) { $renderOnlyOnce = 'row' === $blockNameSuffix || 'widget' === $blockNameSuffix; if ($renderOnlyOnce && $view->isRendered()) { return ''; } // The cache key for storing the variables and types $viewCacheKey = $view->vars[self::CACHE_KEY_VAR]; $viewAndSuffixCacheKey = $viewCacheKey . $blockNameSuffix; // In templates, we have to deal with two kinds of block hierarchies: // // +---------+ +---------+ // | Theme B | -------> | Theme A | // +---------+ +---------+ // // form_widget -------> form_widget // ^ // | // choice_widget -----> choice_widget // // The first kind of hierarchy is the theme hierarchy. This allows to // override the block "choice_widget" from Theme A in the extending // Theme B. This kind of inheritance needs to be supported by the // template engine and, for example, offers "parent()" or similar // functions to fall back from the custom to the parent implementation. // // The second kind of hierarchy is the form type hierarchy. This allows // to implement a custom "choice_widget" block (no matter in which theme), // or to fallback to the block of the parent type, which would be // "form_widget" in this example (again, no matter in which theme). // If the designer wants to explicitly fallback to "form_widget" in his // custom "choice_widget", for example because he only wants to wrap // a <div> around the original implementation, he can simply call the // widget() function again to render the block for the parent type. // // The second kind is implemented in the following blocks. if (!isset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey])) { // INITIAL CALL // Calculate the hierarchy of template blocks and start on // the bottom level of the hierarchy (= "_<id>_<section>" block) $blockNameHierarchy = array(); foreach ($view->vars['block_prefixes'] as $blockNamePrefix) { $blockNameHierarchy[] = $blockNamePrefix . '_' . $blockNameSuffix; } $hierarchyLevel = count($blockNameHierarchy) - 1; $hierarchyInit = true; } else { // RECURSIVE CALL // If a block recursively calls searchAndRenderBlock() again, resume rendering // using the parent type in the hierarchy. $blockNameHierarchy = $this->blockNameHierarchyMap[$viewAndSuffixCacheKey]; $hierarchyLevel = $this->hierarchyLevelMap[$viewAndSuffixCacheKey] - 1; $hierarchyInit = false; } // The variables are cached globally for a view (instead of for the // current suffix) if (!isset($this->variableStack[$viewCacheKey])) { // The default variable scope contains all view variables, merged with // the variables passed explicitly to the helper $scopeVariables = $view->vars; $varInit = true; } else { // Reuse the current scope and merge it with the explicitly passed variables $scopeVariables = end($this->variableStack[$viewCacheKey]); $varInit = false; } // Load the resource where this block can be found $resource = $this->engine->getResourceForBlockNameHierarchy($view, $blockNameHierarchy, $hierarchyLevel); // Update the current hierarchy level to the one at which the resource was // found. For example, if looking for "choice_widget", but only a resource // is found for its parent "form_widget", then the level is updated here // to the parent level. $hierarchyLevel = $this->engine->getResourceHierarchyLevel($view, $blockNameHierarchy, $hierarchyLevel); // The actually existing block name in $resource $blockName = $blockNameHierarchy[$hierarchyLevel]; // Escape if no resource exists for this block if (!$resource) { throw new FormException(sprintf('Unable to render the form as none of the following blocks exist: "%s".', implode('", "', array_reverse($blockNameHierarchy)))); } // Merge the passed with the existing attributes if (isset($variables['attr']) && isset($scopeVariables['attr'])) { $variables['attr'] = array_replace($scopeVariables['attr'], $variables['attr']); } // Merge the passed with the exist *label* attributes if (isset($variables['label_attr']) && isset($scopeVariables['label_attr'])) { $variables['label_attr'] = array_replace($scopeVariables['label_attr'], $variables['label_attr']); } // Do not use array_replace_recursive(), otherwise array variables // cannot be overwritten $variables = array_replace($scopeVariables, $variables); // In order to make recursive calls possible, we need to store the block hierarchy, // the current level of the hierarchy and the variables so that this method can // resume rendering one level higher of the hierarchy when it is called recursively. // // We need to store these values in maps (associative arrays) because within a // call to widget() another call to widget() can be made, but for a different view // object. These nested calls should not override each other. $this->blockNameHierarchyMap[$viewAndSuffixCacheKey] = $blockNameHierarchy; $this->hierarchyLevelMap[$viewAndSuffixCacheKey] = $hierarchyLevel; // We also need to store the variables for the view so that we can render other // blocks for the same view using the same variables as in the outer block. $this->variableStack[$viewCacheKey][] = $variables; // Do the rendering $html = $this->engine->renderBlock($view, $resource, $blockName, $variables); // Clear the stack array_pop($this->variableStack[$viewCacheKey]); // Clear the caches if they were filled for the first time within // this function call if ($hierarchyInit) { unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey]); unset($this->hierarchyLevelMap[$viewAndSuffixCacheKey]); } if ($varInit) { unset($this->variableStack[$viewCacheKey]); } if ($renderOnlyOnce) { $view->setRendered(); } return $html; }
/** * Renders a template. * * 1. This function first looks for a block named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function render(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('widget', 'row')); if ($mainTemplate && $view->isRendered()) { return ''; } if (null === $this->template) { $this->template = reset($this->resources); if (!$this->template instanceof \Twig_Template) { $this->template = $this->environment->loadTemplate($this->template); } } $custom = '_' . $view->get('id'); $rendering = $custom . $section; $blocks = $this->getBlocks($view); if (isset($this->varStack[$rendering])) { $typeIndex = $this->varStack[$rendering]['typeIndex'] - 1; $types = $this->varStack[$rendering]['types']; $this->varStack[$rendering]['variables'] = array_replace_recursive($this->varStack[$rendering]['variables'], $variables); } else { $types = $view->get('types'); $types[] = $custom; $typeIndex = count($types) - 1; $this->varStack[$rendering] = array('variables' => array_replace_recursive($view->all(), $variables), 'types' => $types); } do { $types[$typeIndex] .= '_' . $section; if (isset($blocks[$types[$typeIndex]])) { $this->varStack[$rendering]['typeIndex'] = $typeIndex; // we do not call renderBlock here to avoid too many nested level calls (XDebug limits the level to 100 by default) ob_start(); $this->template->displayBlock($types[$typeIndex], $this->varStack[$rendering]['variables'], $blocks); $html = ob_get_clean(); if ($mainTemplate) { $view->setRendered(); } unset($this->varStack[$rendering]); return $html; } } while (--$typeIndex >= 0); throw new FormException(sprintf('Unable to render the form as none of the following blocks exist: "%s".', implode('", "', array_reverse($types)))); }
/** * Renders a template. * * 1. This function first looks for a function named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', * 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function render(FormView $view, \Smarty_Internal_Template $template, $section, array $variables = array()) { $mainTemplate = in_array($section, array('widget', 'row')); if ($mainTemplate && $view->isRendered()) { return ''; } $this->loadTemplates($view, $template); $custom = '_' . $view->get('id'); $rendering = $custom . $section; if (isset($this->varStack[$rendering])) { $typeIndex = $this->varStack[$rendering]['typeIndex'] - 1; $types = $this->varStack[$rendering]['types']; $this->varStack[$rendering]['variables'] = array_replace_recursive($this->varStack[$rendering]['variables'], $variables); } else { $types = $view->get('types'); $types[] = $custom; $typeIndex = count($types) - 1; $this->varStack[$rendering] = array('variables' => array_replace_recursive($view->all(), $variables), 'types' => $types); } do { $function = $types[$typeIndex] .= '_' . $section; $template = $this->lookupTemplateFunction($function); if ($template) { $this->varStack[$rendering]['typeIndex'] = $typeIndex; ob_start(); $functionExists = $this->engine->renderTemplateFunction($template, $function, $this->varStack[$rendering]['variables']); $html = ob_get_clean(); if ($functionExists) { unset($this->varStack[$rendering]); if ($mainTemplate) { $view->setRendered(); } return $html; } } } while (--$typeIndex >= 0); throw new FormException(sprintf('Unable to render the form as none of the following functions exist: "%s".', implode('", "', array_reverse($types)))); }
/** * Renders a template. * * 1. This function first looks for a block named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function renderSection(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('row', 'widget')); if ($mainTemplate && $view->isRendered()) { return ''; } $template = null; $custom = '_' . $view->get('id'); $rendering = $custom . $section; if (isset($this->varStack[$rendering])) { $typeIndex = $this->varStack[$rendering]['typeIndex'] - 1; $types = $this->varStack[$rendering]['types']; $variables = array_replace_recursive($this->varStack[$rendering]['variables'], $variables); } else { $types = $view->get('types'); $types[] = $custom; $typeIndex = count($types) - 1; $variables = array_replace_recursive($view->all(), $variables); $this->varStack[$rendering]['types'] = $types; } $this->varStack[$rendering]['variables'] = $variables; do { $types[$typeIndex] .= '_' . $section; $template = $this->lookupTemplate($view, $types[$typeIndex]); if ($template) { $this->varStack[$rendering]['typeIndex'] = $typeIndex; $this->context[] = array('variables' => $variables, 'view' => $view); $html = $this->engine->render($template, $variables); array_pop($this->context); unset($this->varStack[$rendering]); if ($mainTemplate) { $view->setRendered(); } return $html; } } while (--$typeIndex >= 0); throw new FormException(sprintf('Unable to render the form as none of the following blocks exist: "%s".', implode('", "', array_reverse($types)))); }
/** * Renders a template. * * 1. This function first looks for a block named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function render(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('widget', 'row')); if ($mainTemplate && $view->isRendered()) { return ''; } $templates = $this->getTemplates($view); $blocks = $view->get('types'); array_unshift($blocks, '_' . $view->get('id')); foreach ($blocks as &$block) { $block = $block . '_' . $section; if (isset($templates[$block])) { $this->varStack[$view] = array_replace($view->all(), isset($this->varStack[$view]) ? $this->varStack[$view] : array(), $variables); $html = $templates[$block]->renderBlock($block, $this->varStack[$view]); if ($mainTemplate) { $view->setRendered(); } unset($this->varStack[$view]); return $html; } } throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks))); }
public function testFinishViewWhenFormBlockIsRoot() { $formLayoutBuilder = $this->getMock('Oro\\Bundle\\LayoutBundle\\Layout\\Form\\FormLayoutBuilderInterface'); $type = new FormType($formLayoutBuilder); $formName = 'form'; $view = new BlockView(); $block = $this->getMock('Oro\\Component\\Layout\\BlockInterface'); $formAccessor = $this->getMock('Oro\\Bundle\\LayoutBundle\\Layout\\Form\\FormAccessorInterface'); $context = new LayoutContext(); $formView = new FormView(); $view->vars['form'] = $formView; $formView->children['field1'] = new FormView($formView); $formView->children['field2'] = new FormView($formView); $field3View = new FormView($formView); $formView->children['field3'] = $field3View; $field3View->children['field31'] = new FormView($field3View); $field3View->children['field32'] = new FormView($field3View); $view->children['block1'] = new BlockView($view); $view->children['block1']->vars['form'] = $formView['field1']; $view->children['block3'] = new BlockView($view); $view->children['block3']->vars['form'] = $field3View['field31']; $this->setLayoutBlocks(['root' => $view]); $context->set('form', $formAccessor); $block->expects($this->once())->method('getContext')->will($this->returnValue($context)); $formAccessor->expects($this->once())->method('getProcessedFields')->will($this->returnValue(['field1' => 'block1', 'field2' => 'block2', 'field3.field31' => 'block3', 'field3.field32' => 'block4'])); $type->finishView($view, $block, ['form_name' => $formName]); $this->assertFalse($formView->isRendered()); $this->assertFalse($formView['field1']->isRendered()); $this->assertTrue($formView['field2']->isRendered()); $this->assertFalse($formView['field3']['field31']->isRendered()); $this->assertTrue($formView['field3']['field32']->isRendered()); }
protected function serializeBlock(\DOMElement $parentElement, FormView $view, $blockName) { $variables = $view->vars; $type = null; foreach ($variables['block_prefixes'] as $blockPrefix) { if (in_array($blockPrefix, static::$baseTypes)) { $type = $blockPrefix; // We use the last found } } if ($view->isRendered()) { return; } if ('rest' == $blockName) { $this->serializeRestWidget($parentElement, $view, $variables); } else { switch ($type) { case 'text': $this->serializeWidgetSimple($parentElement, $view, $variables); break; case 'textarea': $this->serializeTextareaWidget($parentElement, $view, $variables); break; case 'email': $this->serializeEmailWidget($parentElement, $view, $variables); break; case 'integer': $this->serializeIntegerWidget($parentElement, $view, $variables); break; case 'number': $this->serializeNumberWidget($parentElement, $view, $variables); break; case 'password': $this->serializePasswordWidget($parentElement, $view, $variables); break; case 'percent': $this->serializePercentWidget($parentElement, $view, $variables); break; case 'search': $this->serializeSearchWidget($parentElement, $view, $variables); break; case 'url': $this->serializeUrlWidget($parentElement, $view, $variables); break; case 'choice': $this->serializeChoiceWidget($parentElement, $view, $variables); break; case 'hidden': $this->serializeHiddenWidget($parentElement, $view, $variables); break; case 'collection': $this->serializeCollectionWidget($parentElement, $view, $variables); break; case 'checkbox': $this->serializeCheckboxWidget($parentElement, $view, $variables); break; case 'radio': $this->serializeRadioWidget($parentElement, $view, $variables); break; case 'datetime': $this->serializeDatetimeWidget($parentElement, $view, $variables); break; case 'date': $this->serializeDateWidget($parentElement, $view, $variables); break; default: switch ($blockName) { case 'widget': $this->serializeFormWidget($parentElement, $view, $variables); break; case 'row': $this->serializeFormRow($parentElement, $view, $variables); break; default: throw new \RuntimeException(__METHOD__ . ' Oups ' . $view->vars['name'] . ' // ' . $blockName); } } } $view->setRendered(); }
/** * Renders a template. * * 1. This function first looks for a block named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function renderSection(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('row', 'widget')); if ($mainTemplate && $view->isRendered()) { return ''; } $template = null; $types = $view->get('types'); $types[] = '_'.$view->get('proto_id', $view->get('id')); for ($i = count($types) - 1; $i >= 0; $i--) { $types[$i] .= '_'.$section; $template = $this->lookupTemplate($types[$i]); if ($template) { $html = $this->render($view, $template, $variables); if ($mainTemplate) { $view->setRendered(); } return $html; } } throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $types))); }
/** * Renders a template. * * 1. This function first looks for a block named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function render(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('widget', 'row')); if ($mainTemplate && $view->isRendered()) { return ''; } if (null === $this->template) { $this->template = reset($this->resources); if (!$this->template instanceof \Twig_Template) { $this->template = $this->environment->loadTemplate($this->template); } } $blocks = $this->getBlocks($view); $types = $view->get('types'); $types[] = '_'.$view->get('proto_id', $view->get('id')); for ($i = count($types) - 1; $i >= 0; $i--) { $types[$i] .= '_'.$section; if (isset($blocks[$types[$i]])) { $this->varStack[$view] = array_replace( $view->all(), isset($this->varStack[$view]) ? $this->varStack[$view] : array(), $variables ); $html = $this->template->renderBlock($types[$i], $this->varStack[$view], $blocks); if ($mainTemplate) { $view->setRendered(); } unset($this->varStack[$view]); return $html; } } throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $types))); }
protected function render(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('widget', 'row')); if ($mainTemplate && $view->isRendered()) { return ''; } $id = '_' . $view->get('proto_id', $view->get('id')); $template = $id . $section; $renderer = $this->getRenderer(); if (isset($this->varStack[$template])) { $typeIndex = $this->varStack[$template]['typeIndex'] - 1; $types = $this->varStack[$template]['types']; $this->varStack[$template]['variables'] = array_replace_recursive($this->varStack[$template]['variables'], $variables); } else { $types = $view->get('types'); $types[] = $id; $typeIndex = count($types) - 1; $this->varStack[$template] = array('variables' => array_replace_recursive($view->all(), $variables), 'types' => $types); } do { $types[$typeIndex] .= '_' . $section; if (isset($renderer[$types[$typeIndex]])) { $this->varStack[$template]['typeIndex'] = $typeIndex; $html = $renderer[$types[$typeIndex]]->render($view, $this->varStack[$template]['variables'], $this); if ($mainTemplate) { $view->setRendered(); } unset($this->varStack[$template]); return $html; } } while (--$typeIndex >= 0); throw new FormException(sprintf('Unable to render the form as none of the following renderer exist: "%s".', implode('", "', array_reverse($types)))); }
/** * Renders a template. * * 1. This function first looks for a block named "_<view id>_<section>", * 2. if such a block is not found the function will look for a block named * "<type name>_<section>", * 3. the type name is recursively replaced by the parent type name until a * corresponding block is found * * @param FormView $view The form view * @param string $section The section to render (i.e. 'row', 'widget', 'label', ...) * @param array $variables Additional variables * * @return string The html markup * * @throws FormException if no template block exists to render the given section of the view */ protected function renderSection(FormView $view, $section, array $variables = array()) { $mainTemplate = in_array($section, array('row', 'widget')); if ($mainTemplate && $view->isRendered()) { return ''; } $template = null; $blocks = $view->get('types'); array_unshift($blocks, '_' . $view->get('id')); foreach ($blocks as &$block) { $block = $block . '_' . $section; $template = $this->lookupTemplate($block); if ($template) { break; } } if (!$template) { throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks))); } $html = $this->render($view, $template, $variables); if ($mainTemplate) { $view->setRendered(); } return $html; }