class views_field_view_handler_field_view in Views Field View 6
Same name and namespace in other branches
- 7 views_field_view_handler_field_view.inc \views_field_view_handler_field_view
Hierarchy
- class \views_field_view_handler_field_view extends \views_handler_field
Expanded class hierarchy of views_field_view_handler_field_view
1 string reference to 'views_field_view_handler_field_view'
- views_field_view_views_data_alter in ./
views_field_view.views.inc - Implements hook_views_data_alter().
File
- ./
views_field_view_handler_field_view.inc, line 3
View source
class views_field_view_handler_field_view extends views_handler_field {
/**
* If query aggregation is used, all of the arguments for the child view.
*
* This is a multidimensional array containing field_aliases for the argument's fields
* and containing a linear array of all of the results to be used as arguments in various fields.
*/
public $child_arguments = array();
/**
* If query aggregation is used, this attribute contains an array of the results
* of the aggregated child views.
*/
public $child_view_results = array();
/**
* If query aggregation is enabled, one instance of the child view to be reused.
*
* Note, it should never contain arguments or results because they will be
* injected into it for rendering.
*/
public $child_view = false;
function option_definition() {
$options = parent::option_definition();
$options['view'] = array(
'default' => '',
);
$options['display'] = array(
'default' => 'default',
);
$options['arguments'] = array(
'default' => '',
);
$options['query_aggregation'] = array(
'default' => FALSE,
);
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$options = drupal_map_assoc(array_keys(views_get_all_views()));
unset($options[$this->view->name]);
$form['view'] = array(
'#type' => 'select',
'#title' => t('View'),
'#default_value' => $this->options['view'],
'#options' => $options,
);
if ($this->options['view']) {
$view = views_get_view($this->options['view']);
$options = array();
foreach ($view->display as $name => $display) {
$options[$name] = $display->display_title;
}
$form['display'] = array(
'#title' => t('Display'),
'#type' => 'select',
'#default_value' => $this->options['display'],
'#options' => $options,
);
$form['arguments'] = array(
'#title' => t('Arguments'),
'#description' => t('Use a comma-seperated list of each argument which should be forwarded to the view') . $form['alter']['help']['#prefix'],
'#type' => 'textfield',
'#default_value' => $this->options['arguments'],
);
$form['query_aggregation'] = array(
'#title' => t('Aggregate queries'),
'#description' => t('Views Field View usually runs a separate query for each instance of this field on each row and that can mean a lot of queries. This option attempts to aggregate these queries into one query per instance of this field (regardless of how many rows are displayed). <strong>Currently child views must be configured to "display all values" if no argument is present and query aggregation is enabled.</strong>. This may only work on simple views, please test thoroughly.') . $form['alter']['help']['#prefix'],
'#type' => 'checkbox',
'#default_value' => $this->options['query_aggregation'],
);
// Ensure we're working with a SQL view.
if (views_api_version() == '3.0') {
$views_data = views_fetch_data($view->base_table);
if ($views_data['table']['base']['query class'] == 'views_query') {
$form['query_aggregation']['#disabled'] = TRUE;
}
}
}
$form['alter']['#access'] = FALSE;
}
function query() {
$this
->add_additional_fields();
}
/**
* Run before any fields are rendered.
*
* This gives the handlers some time to set up before any handler has
* been rendered.
*
* @param $values
* An array of all objects returned from the query.
*/
function pre_render($values) {
// Only act if we are attempting to aggregate all of the field
// instances into a single query.
if ($this->options['view'] && $this->options['query_aggregation']) {
// Note: Unlike render, pre_render will be run exactly once per
// views_field_view field (not once for each row).
$child_view_name = $this->options['view'];
$child_view_display = $this->options['display'];
// Add each argument token configured for this view_field.
foreach (explode(',', $this->options['arguments']) as $token) {
// Remove the brackets around the token
$argument = substr($token, 1, -1);
// Collect all of the values that we intend to use as arguments of our single query.
if (isset($this->view->field[$argument]->field_alias)) {
$field_alias = $this->view->field[$argument]->field_alias;
foreach ($values as $value) {
if (isset($value->{$field_alias})) {
$this->child_arguments[$field_alias]['argument_name'] = $argument;
$this->child_arguments[$field_alias]['values'][] = $value->{$field_alias};
}
}
}
}
// If we don't have child arguments we should not try to do any of our magic.
if (count($this->child_arguments)) {
// Cache the child_view in this object to minize our calls to views_get_view.
$this->child_view = views_get_view($child_view_name);
$child_view = $this->child_view;
// Set the appropriate display.
$child_view
->access($child_view_display);
// Find the arguments on the child view that we're going to need if the arguments have been overridden.
foreach ($child_view->display['default']->display_options['arguments'] as $argument_name => $argument_value) {
if (isset($child_view->display[$child_view_display]->display_options['arguments'][$argument_name])) {
$configured_arguments[$argument_name] = $child_view->display[$child_view_display]->display_options['arguments'][$argument_name];
}
else {
$configured_arguments[$argument_name] = $child_view->display['default']->display_options['arguments'][$argument_name];
}
}
$argument_ids = array();
foreach ($this->child_arguments as $child_argument_name => $child_argument) {
// Work with the arguments on the child view in the order they are
// specified in our views_field_view field settings.
$configured_argument = array_shift($configured_arguments);
// To be able to later split up our results among the appropriate rows,
// we need to add whatever argument fields we're using to the query.
$argument_ids[$child_argument_name] = $child_view
->add_item($child_view_display, 'field', $configured_argument['table'], $configured_argument['field'], array(
'exclude' => TRUE,
));
// Initialize the query object so that we have it to alter.
// The child view may have been limited but our result set here should not be.
if (isset($child_view->pager['items_per_page'])) {
$child_view->pager['items_per_page'] = 0;
}
$child_view
->build();
$sql = ' IN (';
$i = 0;
foreach ($child_argument['values'] as $argument) {
// TODO: It would be great if this were a bit smarter and if we could
// use the argument handler to build the sql. However, this might be
// irrelevant because any field doing something non-standard would
// probably not work anyway.
if ($i > 0) {
$sql .= ', ';
}
$i++;
if (is_numeric($argument)) {
$sql .= '%n';
}
else {
$sql .= "'%s'";
}
}
$sql .= ')';
// Add the WHERE IN clause to this query.
$child_view->query
->add_where(0, $configured_argument['table'] . '.' . $configured_argument['field'] . $sql, $child_argument['values']);
}
$child_view->build_info['query'] = $child_view->query
->query();
$child_view->build_info['count_query'] = $child_view->query
->query(TRUE);
$child_view->build_info['query_args'] = $child_view->query
->get_where_args();
// Execute the query to retrieve the results.
$child_view
->execute();
// Now that the query has run, we need to get the field alias for each argument field
// so that it can be identified later.
foreach ($argument_ids as $child_argument_name => $argument_id) {
$this->child_arguments[$child_argument_name]['child_view_field_alias'] = $child_view->field[$argument_id]->field_alias;
}
$results = $child_view->result;
// Finally: Cache the results so that they're easily accessible for the render function.
// Loop through the results from the main view so that we can cache the results relevant to each row.
foreach ($values as $value) {
// Add an element to the child_view_results array for each of the rows keyed by this view's base_field.
$this->child_view_results[$value->{$this->view->base_field}] = array();
$child_view_result_row =& $this->child_view_results[$value->{$this->view->base_field}];
// Loop through the actual result set looking for matches to these arguments.
foreach ($results as $result) {
// Assume that we have a matching item until we know that we don't.
$matching_item = TRUE;
// Check each argument that we care about to ensure that it matches.
foreach ($this->child_arguments as $child_argument_field_alias => $child_argument) {
// If one of our arguments does not match the argument of this field, do not add it to this row.
if (isset($value->{$child_argument_field_alias}) && $value->{$child_argument_field_alias} != $result->{$child_argument['child_view_field_alias']}) {
$matching_item = FALSE;
}
}
if ($matching_item) {
$child_view_result_row[] = $result;
}
}
// Make a best effort attempt at paging.
if (isset($this->child_view->pager['items_per_page'])) {
$item_limit = $this->child_view->pager['items_per_page'];
// If the item limit exists but is set to zero, do not split up the results.
if ($item_limit != 0) {
$results = array_chunk($results, $item_limit);
$offset = isset($this->child_view->pager['offset']) ? $this->child_view->pager['offset'] : 0;
$results = $results[$offset];
}
}
unset($child_view_result_row);
}
// We have essentially built and executed the child view member of this view.
// Set it accordingly so that it is not rebuilt during the rendering of each row below.
$this->child_view->built = TRUE;
$this->child_view->executed = TRUE;
}
}
}
function render($values) {
static $running = array();
// Protect against the evil / recursion.
// Set the variable for yourself, this is not for the normal "user".
if (empty($running[$this->options['view']][$this->options['display']]) || variable_get('views_field_view_evil', FALSE)) {
if ($this->options['view'] && !$this->options['query_aggregation']) {
$running[$this->options['view']][$this->options['display']] = TRUE;
$args = array();
$args[] = $this->options['view'];
$args[] = $this->options['display'];
// Only perform this loop if there are actually arguments present
if (!empty($this->options['arguments'])) {
foreach (explode(',', $this->options['arguments']) as $argument) {
$alter = array(
'text' => $argument,
);
$tokens = $this
->get_render_tokens($alter);
$value = $this
->render_altered($alter, $tokens);
$args[] = $value;
}
}
$output = call_user_func_array('views_embed_view', $args);
$running[$this->options['view']][$this->options['display']] = FALSE;
}
else {
if ($this->child_view && $this->options['view'] && $this->options['query_aggregation']) {
$running[$this->options['view']][$this->options['display']] = TRUE;
$child_view = $this->child_view;
$results = $this->child_view_results[$values->{$this->view->base_field}];
// Inject the appropriate result set before rendering the view.
$child_view->result = $results;
if (isset($child_view->style_plugin->rendered_fields)) {
unset($child_view->style_plugin->rendered_fields);
}
$output = $child_view
->render();
$running[$this->options['view']][$this->options['display']] = FALSE;
}
}
}
else {
$output = t('Recursion, stop!');
}
return $output;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
views_field_view_handler_field_view:: |
public | property | If query aggregation is used, all of the arguments for the child view. | |
views_field_view_handler_field_view:: |
public | property | If query aggregation is enabled, one instance of the child view to be reused. | |
views_field_view_handler_field_view:: |
public | property | If query aggregation is used, this attribute contains an array of the results of the aggregated child views. | |
views_field_view_handler_field_view:: |
function | |||
views_field_view_handler_field_view:: |
function | |||
views_field_view_handler_field_view:: |
function | Run before any fields are rendered. | ||
views_field_view_handler_field_view:: |
function | |||
views_field_view_handler_field_view:: |
function |