/** * Tests the mapping of fields. * * @param \Drupal\views\ViewExecutable $view * The view to test. * * @return string * The view rendered as HTML. */ protected function mappedOutputHelper($view) { $output = $view->preview(); $rendered_output = \Drupal::service('renderer')->renderRoot($output); $this->storeViewPreview($rendered_output); $rows = $this->elements->body->div->div->div; $data_set = $this->dataSet(); $count = 0; foreach ($rows as $row) { $attributes = $row->attributes(); $class = (string) $attributes['class'][0]; $this->assertTrue(strpos($class, 'views-row-mapping-test') !== FALSE, 'Make sure that each row has the correct CSS class.'); foreach ($row->div as $field) { // Split up the field-level class, the first part is the mapping name // and the second is the field ID. $field_attributes = $field->attributes(); $name = strtok((string) $field_attributes['class'][0], '-'); $field_id = strtok('-'); // The expected result is the mapping name and the field value, // separated by ':'. $expected_result = $name . ':' . $data_set[$count][$field_id]; $actual_result = (string) $field; $this->assertIdentical($expected_result, $actual_result, format_string('The fields were mapped successfully: %name => %field_id', array('%name' => $name, '%field_id' => $field_id))); } $count++; } return $rendered_output; }
/** * Asserts a view's result & output cache items' cache tags. * * @param \Drupal\views\ViewExecutable $view * The view to test, must have caching enabled. * @param null|string[] $expected_results_cache * NULL when expecting no results cache item, a set of cache tags expected * to be set on the results cache item otherwise. * @param bool $views_caching_is_enabled * Whether to expect an output cache item. If TRUE, the cache tags must * match those in $expected_render_array_cache_tags. * @param string[] $expected_render_array_cache_tags * A set of cache tags expected to be set on the built view's render array. * * @return array * The render array */ protected function assertViewsCacheTags(ViewExecutable $view, $expected_results_cache, $views_caching_is_enabled, array $expected_render_array_cache_tags) { $build = $view->preview(); // Ensure the current request is a GET request so that render caching is // active for direct rendering of views, just like for actual requests. /** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */ $request_stack = \Drupal::service('request_stack'); $request_stack->push(new Request()); \Drupal::service('renderer')->renderRoot($build); $request_stack->pop(); // Render array cache tags. $this->pass('Checking render array cache tags.'); sort($expected_render_array_cache_tags); $this->assertEqual($build['#cache']['tags'], $expected_render_array_cache_tags); if ($views_caching_is_enabled) { $this->pass('Checking Views results cache item cache tags.'); /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */ $cache_plugin = $view->display_handler->getPlugin('cache'); // Results cache. $results_cache_item = \Drupal::cache('data')->get($cache_plugin->generateResultsKey()); if (is_array($expected_results_cache)) { $this->assertTrue($results_cache_item, 'Results cache item found.'); if ($results_cache_item) { sort($expected_results_cache); $this->assertEqual($results_cache_item->tags, $expected_results_cache); } } else { $this->assertFalse($results_cache_item, 'Results cache item not found.'); } // Output cache. $this->pass('Checking Views output cache item cache tags.'); $output_cache_item = \Drupal::cache('render')->get($cache_plugin->generateOutputKey()); if ($views_caching_is_enabled === TRUE) { $this->assertTrue($output_cache_item, 'Output cache item found.'); if ($output_cache_item) { $this->assertEqual($output_cache_item->tags, Cache::mergeTags($expected_render_array_cache_tags, ['rendered'])); } } else { $this->assertFalse($output_cache_item, 'Output cache item not found.'); } } $view->destroy(); return $build; }
public function renderPreview($display_id, $args = array()) { // Save the current path so it can be restored before returning from this function. $old_q = current_path(); // Determine where the query and performance statistics should be output. $config = \Drupal::config('views.settings'); $show_query = $config->get('ui.show.sql_query.enabled'); $show_info = $config->get('ui.show.preview_information'); $show_location = $config->get('ui.show.sql_query.where'); $show_stats = $config->get('ui.show.performance_statistics'); if ($show_stats) { $show_stats = $config->get('ui.show.sql_query.where'); } $combined = $show_query && $show_stats; $rows = array('query' => array(), 'statistics' => array()); $output = ''; $errors = $this->executable->validate(); $this->executable->destroy(); if (empty($errors)) { $this->ajax = TRUE; $this->executable->live_preview = TRUE; // AJAX happens via HTTP POST but everything expects exposed data to // be in GET. Copy stuff but remove ajax-framework specific keys. // If we're clicking on links in a preview, though, we could actually // have some input in the query parameters, so we merge request() and // query() to ensure we get it all. $exposed_input = array_merge(\Drupal::request()->request->all(), \Drupal::request()->query->all()); foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state', 'form_id', 'form_build_id', 'form_token') as $key) { if (isset($exposed_input[$key])) { unset($exposed_input[$key]); } } $this->executable->setExposedInput($exposed_input); if (!$this->executable->setDisplay($display_id)) { return t('Invalid display id @display', array('@display' => $display_id)); } $this->executable->setArguments($args); // Store the current view URL for later use: if ($this->executable->display_handler->getOption('path')) { $path = $this->executable->getUrl(); } // Make view links come back to preview. $this->override_path = 'admin/structure/views/view/' . $this->id() . '/preview/' . $display_id; // Also override the current path so we get the pager. $original_path = current_path(); $q = _current_path($this->override_path); if ($args) { $q .= '/' . implode('/', $args); _current_path($q); } // Suppress contextual links of entities within the result set during a // Preview. // @todo We'll want to add contextual links specific to editing the View, so // the suppression may need to be moved deeper into the Preview pipeline. views_ui_contextual_links_suppress_push(); $show_additional_queries = $config->get('ui.show.additional_queries'); Timer::start('entity.view.preview_form'); if ($show_additional_queries) { $this->startQueryCapture(); } // Execute/get the view preview. $preview = $this->executable->preview($display_id, $args); $preview = drupal_render($preview); if ($show_additional_queries) { $this->endQueryCapture(); } $this->render_time = Timer::stop('entity.view.preview_form'); views_ui_contextual_links_suppress_pop(); // Reset variables. unset($this->override_path); _current_path($original_path); // Prepare the query information and statistics to show either above or // below the view preview. if ($show_info || $show_query || $show_stats) { // Get information from the preview for display. if (!empty($this->executable->build_info['query'])) { if ($show_query) { $query_string = $this->executable->build_info['query']; // Only the sql default class has a method getArguments. $quoted = array(); if ($this->executable->query instanceof Sql) { $quoted = $query_string->getArguments(); $connection = Database::getConnection(); foreach ($quoted as $key => $val) { if (is_array($val)) { $quoted[$key] = implode(', ', array_map(array($connection, 'quote'), $val)); } else { $quoted[$key] = $connection->quote($val); } } } $rows['query'][] = array(array('data' => array('#type' => 'inline_template', '#template' => "<strong>{% trans 'Query' %}</strong>")), array('data' => array('#type' => 'inline_template', '#template' => '<pre>{{ query }}</pre>', '#context' => array('query' => strtr($query_string, $quoted))))); if (!empty($this->additionalQueries)) { $queries = '<strong>' . t('These queries were run during view rendering:') . '</strong>'; foreach ($this->additionalQueries as $query) { if ($queries) { $queries .= "\n"; } $query_string = strtr($query['query'], $query['args']); $queries .= t('[@time ms] @query', array('@time' => round($query['time'] * 100000, 1) / 100000.0, '@query' => $query_string)); } $rows['query'][] = array(array('data' => array('#type' => 'inline_template', '#template' => "<strong>{% trans 'Other queries' %}</strong>")), SafeMarkup::set('<pre>' . $queries . '</pre>')); } } if ($show_info) { $rows['query'][] = array(array('data' => array('#type' => 'inline_template', '#template' => "<strong>{% trans 'Title' %}</strong>")), Xss::filterAdmin($this->executable->getTitle())); if (isset($path)) { $path = _l($path, $path); } else { $path = t('This display has no path.'); } $rows['query'][] = array(SafeMarkup::set('<strong>' . t('Path') . '</strong>'), $path); } if ($show_stats) { $rows['statistics'][] = array('<strong>' . t('Query build time') . '</strong>', t('@time ms', array('@time' => intval($this->executable->build_time * 100000) / 100))); $rows['statistics'][] = array('<strong>' . t('Query execute time') . '</strong>', t('@time ms', array('@time' => intval($this->executable->execute_time * 100000) / 100))); $rows['statistics'][] = array('<strong>' . t('View render time') . '</strong>', t('@time ms', array('@time' => intval($this->executable->render_time * 100000) / 100))); } \Drupal::moduleHandler()->alter('views_preview_info', $rows, $this->executable); } else { // No query was run. Display that information in place of either the // query or the performance statistics, whichever comes first. if ($combined || $show_location === 'above') { $rows['query'] = array(array(SafeMarkup::set('<strong>' . t('Query') . '</strong>'), t('No query was run'))); } else { $rows['statistics'] = array(array(SafeMarkup::set('<strong>' . t('Query') . '</strong>'), t('No query was run'))); } } } } else { foreach ($errors as $display_errors) { foreach ($display_errors as $error) { drupal_set_message($error, 'error'); } } $preview = t('Unable to preview due to validation errors.'); } // Assemble the preview, the query info, and the query statistics in the // requested order. $table = array('#type' => 'table', '#prefix' => '<div class="views-query-info">', '#suffix' => '</div>'); if ($show_location === 'above' || $show_location === 'below') { if ($combined) { $table['#rows'] = array_merge($rows['query'], $rows['statistics']); } else { $table['#rows'] = $rows['query']; } } elseif ($show_stats === 'above' || $show_stats === 'below') { $table['#rows'] = $rows['statistics']; } if ($show_location === 'above' || $show_stats === 'above') { $output .= drupal_render($table) . $preview; } elseif ($show_location === 'below' || $show_stats === 'below') { $output .= $preview . drupal_render($table); } _current_path($old_q); return $output; }