I'm a bit clueless as to what I'm doing here but this is what I have currently...


/**
 * Generic views handler filter to add code to manipulate the query object.
 */
class drfilter_handler_filter extends views_handler_filter_numeric {
  function can_expose() {
    return TRUE;
  }

  /**
   * Provide a list of all operators.
   */
  function fields_operator_options() {
    return array(
      '-' => t('minus'),
      '+' => t('plus'),
    );
  }

  /**
   * Overrides views_handler_filter#options_form().
   */
  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);

    $form['operator'] = array(
      '#type' => 'select',
      '#title' => t('Operator'),
      '#default_value' => $this->options['operator'],
      '#options' => $this->fields_operator_options(),
      '#weight' => -2,
    );
  }
  
  
  /**
   *
   */
  function pre_render(&$values) {    
    $s = $values[0]->field_data_field_ol_locator_geofield_field_geofield_distance;
    $r = $values[0]->field_field_radius[0]['raw']['value'];
    
    switch ($this->options['operator'])
    {
      case '-':
        $this->value['min'] = $s - $r;
        $this->value['value'] = $s - $r;
        $this->value['max'] = $s - $r;
      break;
    
      case '+':
        $this->value['min'] = $s + $r;
        $this->value['value'] = $s + $r;
        $this->value['max'] = $s + $r;
      break;
    
      default:
      break;
    }
    
  }
  
  /**
   * Overrides views_handler_filter#admin_summary().
   */
  function admin_summary() {
    return check_plain(
      $this->options['operator']
    );
  }
}

Now being naive to the ways of views, this is how I'm expecting this to work.

1. I have the filter exposed with grouped filters.
2. When I set $this->value['value'] = $x; I'm expecting that the numerical field will be evaluated against $x and if the condition (e.g. > 5) is not satisfied then the row will be removed from the results.

Am I totally in the wrong domain here, in trying to achieve what I want?

Comments

rudiedirkx’s picture

This filter doesn't do anything. If pre_render() is executed (I don't think filter handlers have a pre_render()?) it only sets $this->value. It never uses that value.

Are you trying to manipulate the db query? You'll need to override handler::query() to manipulate $this->query, probably using the operator and stuff from your geofield and redius field.

Most Views filter handlers will use query() to manipulate the db query to limit the results. It's possible to limit the results after executing the query, but that's probably not the most efficient method. If you know what the filter should add to the query (what the SQL looks like), we could go from there.

el_toro’s picture

Yes the pre_render() does execute but, as you say, has no effect... I'm not sure how I could manipulate the query as the distance value ($s) is not present in the database but is computed for each result (after being retrieved from the database).

EDIT: The distance is computed relative to a point that the user specifies in another filter.

rudiedirkx’s picture

If you don't want to manipulate the query, what do you want the filter to do? Your code doesn't really explain... Do you want to filter results based on the distance? If you can't do that in the db, you can use post_execute() to filter $this->view->result. I think.

el_toro’s picture

Hey,

I was able to get it working. The formula I was trying to execute to filter was (Distance - Radius) < x. I ended up using a combination of these filters: geofield_handler_filter, views_handler_filter_fields_compare and views_filters_populate_handler_filter.

Here is what the code looks like:

/**
 * Generic views handler filter to add code to manipulate the query object.
 */
class drfilter_handler_filter extends geofield_handler_filter {
  
  
  /**
   * Provide a list of available fields.
   */
  function field_options() {
    $options = array();

    $field_handlers = $this->view->display_handler->get_handlers('field');
    foreach ($field_handlers as $field => $handler) {
      if ($handler->table != 'views') {
        $options[$field] = $handler->ui_name();
      }
    }

    return $options;
  }

  function query() {
    $left = $this->options['left_field'];

    // Get all existing field handlers.
    $field_handlers = $this->view->display_handler->get_handlers('field');

    // Make sure the selected fields still exist.
    if (!isset($field_handlers[$left])) {
      return;
    }

    // Get the left table and field.
    $left_handler = $field_handlers[$left];
    
    $left_handler->set_relationship();
    $left_table_alias = $this->query->ensure_table($left_handler->table, $left_handler->relationship);
    
    $proximityPlugin = geofield_proximity_load_plugin($this->options['source']);
    $options = $proximityPlugin->getSourceValue($this);

    if ($options) {
      $lat_alias = $left_handler->definition['field_name'] . '_lat';
      $lon_alias = $left_handler->definition['field_name'] . '_lon';

      $info = $this->operators();
      if (!empty($info[$this->operator]['method'])) {
        $haversine_options = array(
          'origin_latitude' => $options['latitude'],
          'origin_longitude' => $options['longitude'],
          'destination_latitude' => $left_table_alias . '.' . $lat_alias,
          'destination_longitude' => $left_table_alias . '.' . $lon_alias,
          'earth_radius' => $this->value['unit'],
        );
        $this->{$info[$this->operator]['method']}($haversine_options);
      }
    }
  }

  function op_simple($options) {
    $right = $this->options['right_field'];
    // Get all existing field handlers.
    $field_handlers = $this->view->display_handler->get_handlers('field');

    // Make sure the selected fields still exist.
    if (!isset($field_handlers[$right])) {
      return;
    }

    // Get the left table and field.
    $right_handler = $field_handlers[$right];
    $right_handler->set_relationship();
    $right_table_alias = $this->query->ensure_table($right_handler->table, $right_handler->relationship);
    
    $this->query->add_where_expression(
      $this->options['group'], 
      '(' . geofield_haversine($options) . " - {$right_table_alias}.{$right_handler->real_field})"
        . $this->operator . ' ' . $this->value['distance']
    );
  }

  function option_definition() {
    $options = parent::option_definition();
    // Data sources and info needed.
    $options['source'] = array('default' => 'manual');
    $options['value'] = array(
      'default' => array(
        'distance' => 100,
        'distance2' => 200,
        'unit' => GEOFIELD_KILOMETERS,
        'origin' => array(),
      ),
    );
    $proximityHandlers = geofield_proximity_views_handlers();
    foreach ($proximityHandlers as $key => $handler) {
      $proximityPlugin = new $handler['class']();
      $proximityPlugin->option_definition($options, $this);
    }
    $options['left_field'] = $options['right_field'] = array('default' => '');
    $options['filter'] = array('default' => array());
    return $options;
  }

  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $form['source'] = array(
      '#type' => 'select',
      '#title' => t('Source of Origin Point'),
      '#description' => t('How do you want to enter your origin point?'),
      '#options' => array(),
      '#attached' => array(
        'js' => array(
          drupal_get_path('module', 'geofield') . '/js/viewsProximityValue.js',
        ),
      ),
      '#default_value' => $this->options['source'],
    );

    $form['source_change'] = array(
      '#type' => 'submit',
      '#value' => 'Change Source Widget',
      '#submit' => array('geofield_views_ui_change_proximity_widget'),
    );

    $proximityHandlers = geofield_proximity_views_handlers();
    foreach ($proximityHandlers as $key => $handler) {
      // Manually skip 'Exposed Filter', since it wouldn't make any sense in this context.
      if ($key != 'exposed_geofield_filter') {
        $form['source']['#options'][$key] = $handler['name'];

        $proximityPlugin = new $handler['class']();
        $proximityPlugin->options_form($form, $form_state, $this);
      }
    }

    // Look for any top-level item with a #proximity_plugin_value_element set. If found, it doesn't
    // belong in this particular field.
    foreach ($form as $key =>$form_item) {
      if (isset($form_item['#proximity_plugin_value_element']) && $form_item['#proximity_plugin_value_element'] == TRUE) {
        unset($form[$key]);
      }
    }
    
    $field_options = $this->field_options();
    $form['left_field'] = array(
      '#type' => 'select',
      '#title' => t('Left Field'),
      '#default_value' => $this->options['left_field'],
      '#options' => $field_options,
    );
    $form['right_field'] = array(
      '#type' => 'select',
      '#title' => t('Right Field'),
      '#default_value' => $this->options['right_field'],
      '#options' => $field_options,
    );
    
    foreach((array)$this->view->display_handler->get_handlers('filter') as $id => $filter) {
      if ($id != $this->options['id']) {
        $options[$id] = t($filter->definition['group']) . ':' . t($filter->definition['title']);
      }
    }
    
    $form['filter'] = array(
      '#type' => 'select',
      '#options' => $options,
      '#title' => t('Chained Filter'),
      '#description' => t('Choose the filters whose value will be replaced by the one specified here.'),
      '#default_value' => $this->options['filter'],
    );
  }

  function admin_summary() {
    if (!empty($this->options['exposed'])) {
      return t('exposed');
    }

    $options = $this->operator_options('short');
    $output = check_plain($options[$this->operator]);
    $output .= check_plain($this->options['left_field']);
    $output .= check_plain($this->options['right_field']);
    $output .= check_plain($this->options['filter']);
    if (in_array($this->operator, $this->operator_values(2))) {
      $output .= ' ' . t('@min and @max', array('@min' => $this->value['distance'], '@max' => $this->value['distance2']));
    }
    elseif (in_array($this->operator, $this->operator_values(1))) {
      $output .= ' ' . check_plain($this->value['distance']);
    }
    return $output;
  }

  /**
   * Check to see if input from the exposed filters should change
   * the behavior of this filter.
   *   - @TODO: This could be more polished.
   */
  function accept_exposed_input($input) {
    if (!(isset($this->options['expose']) && isset($this->options['expose']['identifier']))) {
      return FALSE;
    }

    $input_id = $this->options['expose']['identifier'];
    if (empty($input[$input_id]) || $input[$input_id]['distance'] === '' || $input[$input_id]['origin'] === '') {
      $this->value['distance'] = $input[$input_id]['distance'];
      $this->value['unit'] = $input[$input_id]['unit'];
      $this->value['origin'] = $this->view->filter[$this->options['filter']]->value['origin'];
      return TRUE;
    }

    $this->value['distance'] = $input[$input_id]['distance'];
    $this->value['unit'] = $input[$input_id]['unit'];
    $this->value['origin'] = $input[$input_id]['origin'];
    return TRUE;
  }
}

Thanks for taking time to help me on this! I appreciate it.

rudiedirkx’s picture

Status: Active » Closed (fixed)

No problem.