Commit 714b3320 authored by tim.plunkett's avatar tim.plunkett

Issue #1765824 by tim.plunkett: Added a way to map views fields to a certain meaning.

parent 4b273303
<?php
/**
* @file
* Definition of Drupal\views\Plugin\views\style\Mapping.
*/
namespace Drupal\views\Plugin\views\style;
/**
* Allows fields to be mapped to specific use cases.
*
* @ingroup views_style_plugins
*/
abstract class Mapping extends StylePluginBase {
/**
* Do not use grouping.
*
* @var bool
*/
protected $usesGrouping = FALSE;
/**
* Use fields without a row plugin.
*
* @var bool
*/
protected $usesFields = TRUE;
/**
* Builds the list of field mappings.
*
* @return array
* An associative array, keyed by the field name, containing the following
* key-value pairs:
* - #title: The human-readable label for this field.
* - #default_value: The default value for this field. If not provided, an
* empty string will be used.
* - #description: A description of this field.
* - #required: Whether this field is required.
* - #filter: (optional) A method on the plugin to filter field options.
* - #toggle: (optional) If this select should be toggled by a checkbox.
*/
protected function defineMapping() {
return array();
}
/**
* Overrides Drupal\views\Plugin\views\style\StylePluginBase::defineOptions().
*/
protected function defineOptions() {
$options = parent::defineOptions();
// Parse the mapping and add a default for each.
foreach ($this->defineMapping() as $key => $value) {
$default = !empty($value['#multiple']) ? array() : '';
$options['mapping']['contains'][$key] = array(
'default' => isset($value['#default_value']) ? $value['#default_value'] : $default,
);
if (!empty($value['#toggle'])) {
$options['mapping']['contains']["toggle_$key"] = array(
'default' => FALSE,
'bool' => TRUE,
);
}
}
return $options;
}
/**
* Overrides Drupal\views\Plugin\views\style\StylePluginBase::buildOptionsForm().
*/
public function buildOptionsForm(&$form, &$form_state) {
parent::buildOptionsForm($form, $form_state);
// Get the mapping.
$mapping = $this->defineMapping();
// Restrict the list of defaults to the mapping, in case they have changed.
$options = array_intersect_key($this->options['mapping'], $mapping);
// Get the labels of the fields added to this display.
$field_labels = $this->display->handler->getFieldLabels();
// Provide some default values.
$defaults = array(
'#type' => 'select',
'#required' => FALSE,
'#multiple' => FALSE,
);
// For each mapping, add a select element to the form.
foreach ($options as $key => $value) {
// If the field is optional, add a 'None' value to the top of the options.
$field_options = array();
$required = !empty($mapping[$key]['#required']);
if (!$required && empty($mapping[$key]['#multiple'])) {
$field_options = array('' => t('- None -'));
}
$field_options += $field_labels;
// Optionally filter the available fields.
if (isset($mapping[$key]['#filter'])) {
$this->view->initHandlers();
$this::$mapping[$key]['#filter']($field_options);
unset($mapping[$key]['#filter']);
}
// These values must always be set.
$overrides = array(
'#options' => $field_options,
'#default_value' => $options[$key],
);
// Optionally allow the select to be toggleable.
if (!empty($mapping[$key]['#toggle'])) {
$form['mapping']["toggle_$key"] = array(
'#type' => 'checkbox',
'#title' => t('Use a custom %field_name', array('%field_name' => strtolower($mapping[$key]['#title']))),
'#default_value' => $this->options['mapping']["toggle_$key"],
);
$overrides['#states']['visible'][':input[name="style_options[mapping][' . "toggle_$key" . ']"]'] = array('checked' => TRUE);
}
$form['mapping'][$key] = $overrides + $mapping[$key] + $defaults;
}
}
/**
* Overrides Drupal\views\Plugin\views\style\StylePluginBase::render().
*
* Provides the mapping definition as an available variable.
*/
function render() {
return theme($this->themeFunctions(), array(
'view' => $this->view,
'options' => $this->options,
'rows' => $this->view->result,
'mapping' => $this->defineMapping(),
));
}
}
<?php
/**
* @file
* Definition of Drupal\views\Tests\Plugin\StyleMappingTest.
*/
namespace Drupal\views\Tests\Plugin;
/**
* Tests the default/mapping row style.
*/
class StyleMappingTest extends StyleTestBase {
public static function getInfo() {
return array(
'name' => 'Style: Mapping',
'description' => 'Test mapping style functionality.',
'group' => 'Views Plugins',
);
}
/**
* Overrides Drupal\views\Tests\ViewTestBase::getBasicView().
*/
protected function getBasicView() {
return $this->createViewFromConfig('test_style_mapping');
}
/**
* Verifies that the fields were mapped correctly.
*/
public function testMappedOutput() {
$view = $this->getView();
$output = $this->mappedOutputHelper($view);
$this->assertTrue(strpos($output, 'job') === FALSE, 'The job field is added to the view but not in the mapping.');
$view = $this->getView();
$view->display['default']->handler->options['style_options']['mapping']['name_field'] = 'job';
$output = $this->mappedOutputHelper($view);
$this->assertTrue(strpos($output, 'job') !== FALSE, 'The job field is added to the view and is in the mapping.');
}
/**
* Tests the mapping of fields.
*
* @param Drupal\views\View $view
* The view to test.
*
* @return string
* The view rendered as HTML.
*/
protected function mappedOutputHelper($view) {
$rendered_output = $view->preview();
$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;
}
}
......@@ -8,12 +8,11 @@
namespace Drupal\views\Tests\Plugin;
use stdClass;
use DOMDocument;
/**
* Tests some general style plugin related functionality.
*/
class StyleTest extends PluginTestBase {
class StyleTest extends StyleTestBase {
public static function getInfo() {
return array(
......@@ -23,12 +22,6 @@ public static function getInfo() {
);
}
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
}
/**
* Tests the grouping legacy features of styles.
*/
......@@ -244,20 +237,6 @@ function _testGrouping($stripped = FALSE) {
}
}
/**
* Stores a view output in the elements.
*/
function storeViewPreview($output) {
$htmlDom = new DOMDocument();
@$htmlDom->loadHTML($output);
if ($htmlDom) {
// It's much easier to work with simplexml than DOM, luckily enough
// we can just simply import our DOM tree.
$this->elements = simplexml_import_dom($htmlDom);
}
}
/**
* Tests custom css classes.
*/
......
<?php
/**
* @file
* Definition of Drupal\views\Tests\Plugin\StyleTestBase.
*/
namespace Drupal\views\Tests\Plugin;
use DOMDocument;
/**
* Tests some general style plugin related functionality.
*/
abstract class StyleTestBase extends PluginTestBase {
/**
* Stores the SimpleXML representation of the output.
*
* @var SimpleXMLElement
*/
protected $elements;
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
}
/**
* Stores a view output in the elements.
*/
function storeViewPreview($output) {
$htmlDom = new DOMDocument();
@$htmlDom->loadHTML($output);
if ($htmlDom) {
// It's much easier to work with simplexml than DOM, luckily enough
// we can just simply import our DOM tree.
$this->elements = simplexml_import_dom($htmlDom);
}
}
}
......@@ -7,19 +7,10 @@
namespace Drupal\views\Tests\Plugin;
use DOMDocument;
/**
* Tests the default/unformatted row style.
*/
class StyleUnformattedTest extends PluginTestBase {
/**
* Stores all created nodes.
*
* @var array
*/
protected $nodes;
class StyleUnformattedTest extends StyleTestBase {
public static function getInfo() {
return array(
......@@ -29,25 +20,6 @@ public static function getInfo() {
);
}
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
}
/**
* Stores a view output in the elements.
*/
function storeViewPreview($output) {
$htmlDom = new DOMDocument();
@$htmlDom->loadHTML($output);
if ($htmlDom) {
// It's much easier to work with simplexml than DOM, luckily enough
// we can just simply import our DOM tree.
$this->elements = simplexml_import_dom($htmlDom);
}
}
/**
* Take sure that the default css classes works as expected.
*/
......
api_version: '3.0'
base_table: views_test
core: '8'
description: ''
disabled: '0'
display:
default:
display_options:
defaults:
fields: '0'
pager: '0'
pager_options: '0'
sorts: '0'
fields:
age:
field: age
id: age
relationship: none
table: views_test
job:
field: job
id: job
relationship: none
table: views_test
name:
field: name
id: name
relationship: none
table: views_test
pager:
options:
offset: '0'
type: none
pager_options: { }
sorts:
id:
field: id
id: id
order: ASC
relationship: none
table: views_test
style_options:
mapping:
name_field: name
numeric_field:
age: age
title_field: name
toggle_numeric_field: '1'
toggle_title_field: '1'
style_plugin: mapping_test
display_plugin: default
display_title: Master
id: default
position: '0'
human_name: ''
name: test_style_mapping
tag: ''
<?php
/**
* @file
* Definition of Drupal\views_test_data\Plugin\views\style\MappingTest;
*/
namespace Drupal\views_test_data\Plugin\views\style;
use Drupal\views\Plugin\views\style\Mapping;
use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
use Drupal\views\Plugin\views\field\Numeric;
/**
* Provides a test plugin for the mapping style.
*
* @ingroup views_style_plugins
*
* @Plugin(
* id = "mapping_test",
* title = @Translation("Field mapping"),
* help = @Translation("Maps specific fields to specific purposes."),
* theme = "views_view_mapping_test",
* type = "normal"
* )
*/
class MappingTest extends Mapping {
/**
* Overrides Drupal\views\Plugin\views\style\Mapping::defineMapping().
*/
protected function defineMapping() {
return array(
'title_field' => array(
'#title' => t('Title field'),
'#description' => t('Choose the field with the custom title.'),
'#toggle' => TRUE,
'#required' => TRUE,
),
'name_field' => array(
'#title' => t('Name field'),
'#description' => t('Choose the field with the custom name.'),
),
'numeric_field' => array(
'#title' => t('Numeric field'),
'#description' => t('Select one or more numeric fields.'),
'#multiple' => TRUE,
'#toggle' => TRUE,
'#filter' => 'filterNumericFields',
'#required' => TRUE,
),
);
}
/**
* Restricts the allowed fields to only numeric fields.
*
* @param array $fields
* An array of field labels, keyed by the field ID.
*/
protected function filterNumericFields(&$fields) {
foreach ($this->view->field as $id => $field) {
if (!($field instanceof Numeric)) {
unset($fields[$id]);
}
}
}
}
......@@ -68,3 +68,59 @@ function views_test_data_views_post_build(View &$view) {
}
}
}
/**
* Implements hook_preprocess_HOOK() for theme_views_view_mapping_test().
*/
function template_preprocess_views_view_mapping_test(&$variables) {
$variables['element'] = array();
foreach ($variables['rows'] as $delta => $row) {
$fields = array();
foreach ($variables['options']['mapping'] as $type => $field_names) {
if (!is_array($field_names)) {
$field_names = array($field_names);
}
foreach ($field_names as $field_name) {
if ($value = $variables['view']->style_plugin->get_field($delta, $field_name)) {
$fields[$type . '-' . $field_name] = $type . ':' . $value;
}
}
}
// If there are no fields in this row, skip to the next one.
if (empty($fields)) {
continue;
}
// Build a container for the row.
$variables['element'][$delta] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'views-row-mapping-test',
),
),
);
// Add each field to the row.
foreach ($fields as $key => $render) {
$variables['element'][$delta][$key] = array(
'#children' => $render,
'#type' => 'container',
'#attributes' => array(
'class' => array(
$key,
),
),
);
}
}
}
/**
* Returns HTML for the Mapping Test style.
*/
function theme_views_view_mapping_test($variables) {
return drupal_render($variables['element']);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment