Currently the views SQL class 'core/modules/views/src/Plugin/views/query/Sql.php Sql' has limitations in the sense that all joins, order by, group by, expressions, etc must belong to a group or field.

It would be nice to be able to have the same freedom when building/modifying queries as the Select class offers. (core/lib/Drupal/Core/Database/Query/Select.php Select)

eg. there is currently no way to call "addExpression" via hook_views_query_alter.

Yes we can call "addHavingExpression" or "addWhereExpression", however no way to add a select expression that is not related to a field.

Something like this would do the trick

public function addCustomExpression($string, $alias){
    $this->customExpressions[] = [
      'string' => $string,
      'alias' => $alias
    ];
  }

Then when building the query, something like this

protected function compileFields($query) {
    if(count($this->customExpressions) > 0){
      foreach($this->customExpressions as $ex){
        $query->addExpression($ex['string'], $ex['alias']);
      }
    }

Then of course a programmer may wish to order by this field, so we'd need some sort of custom order by to order by the field. Perhaps something like the below

public function addCustomOrderBy($field, $direction){
    $this->customOrderBys[] = [
      'field' => $field,
      'direction' => $direction
    ];
  }

Then inside the 'compileFields' method.

if(count($this->customOrderBys) > 0){
      foreach($this->customOrderBys as $ob){
        $query->orderBy($ob->field, $ob->direction);
      }
    }
}

I understand views is field based, however it would be nice to give developers the freedom to manipulate the sql to their choosing.

Cheers.

Issue fork drupal-2920153

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Andrew211 created an issue. See original summary.

Andrew211’s picture

Issue summary: View changes

Version: 8.3.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Branches prior to 8.8.x are not supported, and Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

dravenk’s picture

Version: 8.9.x-dev » 9.1.x-dev
dravenk’s picture

I using this way to solve my problem. Hope to help anybody.


/**
 * Implementation of hook_views_query_alter
 */
function  mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query)  {
  if($view->id() == 'my_view_name') {
    // delete orderby which exist sort by title.
    $query->orderby = [];
    $query->orderby[] = [
      'field' => 'custom_name_of_field',
      'direction' => 'ASC'
    ];
  }
}

function mymodule_views_pre_execute(&$view) {
  if($view->id() == 'my_view_name') {
    $view->build_info['query']->addExpression('CONVERT(node_field_data.title USING GBK)','custom_name_of_field');
  }
}

matt.hood made their first commit to this issue’s fork.

Version: 9.1.x-dev » 9.3.x-dev

Drupal 9.1.10 (June 4, 2021) and Drupal 9.2.10 (November 24, 2021) were the last bugfix releases of those minor version series. Drupal 9 bug reports should be targeted for the 9.3.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

scott_euser’s picture

Just an update on @dravenk's example, here is how the code looks now in D10 + including some WHERE condition examples.


use Drupal\views\ViewExecutable;

/**
 * Implements hook_views_pre_execute().
 */
function mymodule_views_pre_execute(ViewExecutable $view) {
  if($view->id() == 'my_view_name') {
    $query = &$view->build_info['query'];

    // Add a SELECT expression.
    $query->addExpression('CONVERT(node_field_data.title USING GBK)','custom_name_of_field');

    // Add a WHERE expression in MySQL.
    $query->addWhereExpression(0, "COALESCE(some_alias.date, '1900-01-01') < :some_value", [
      ':some_value' => $value_here,
    ]);

    // Add a WHERE expression in SQLite.
    $query->where("COALESCE(some_alias.date, '1900-01-01') < :some_value", [
      ':some_value' => $value_here,
    ]);
  }
}

electric.larry’s picture

Thank you @scott_euser and @dravenk for your great examples!

I landed here looking for a way to implement natural sorting of a text field in a view.

My workaround was to use LPAD on the text field and then sort by this field.


/**
 * @file
 * Custom module to implement natural sort order for staff order views.
 */

use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\query\Sql;
/**
 * Implements hook_views_query_alter().
 *
 * Alters the query for the "personalbestellungen" view to sort by
 * the custom sort field.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view executable object.
 * @param \Drupal\views\Plugin\views\query\Sql $query
 *   The query object for the view.
 */
function wet_personalbestellung_views_query_alter(ViewExecutable $view, Sql $query) {
  if ($view->id() == "personalbestellungen" && $view->current_display === 'page_2') {
    // Add the custom sorting field to the view's query.
    array_unshift($query->orderby, [
      'field' => 'custom_sort_order',
      'direction' => 'DESC',
    ]);
  }
}

/**
 * Implements hook_views_pre_execute().
 *
 * Adds a custom expression for sorting in the "personalbestellungen" view.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view executable object.
 */
function wet_personalbestellung_views_pre_execute(ViewExecutable $view) {
  if ($view->id() == "personalbestellungen" && $view->current_display === 'page_2') {
    // Prepend zeros to the order_number, to mimic natural sorting.
    $view->build_info['query']->addExpression("LPAD(order_number, 100, '0')",'custom_sort_order');
  }
}

Rethinking this problem I ended up with another approach. Instead of prepending the number with endless zeros, I just converted the string value to a number and used this number field for sorting.

 /**
 * Implements hook_views_pre_execute().
 *
 * Adds a custom expression to allow natural sorting in the "personalbestellungen" view.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view executable object.
 */
function wet_personalbestellung_views_pre_execute(ViewExecutable $view) {
  if ($view->id() == "personalbestellungen" && $view->current_display === 'page_2') {
    // Convert the number string to a real number.
    $view->build_info['query']->addExpression("CONVERT(order_number, unsigned)",'custom_sort_order');
  }
}

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.