Commit 78e75789 authored by tim.plunkett's avatar tim.plunkett

Issue #1792800 by tim.plunkett: Move code out of handlers.inc.

parent 2daec13e
This diff is collapsed.
......@@ -7,6 +7,8 @@
namespace Drupal\views;
use Drupal\views\Plugin\views\HandlerBase;
/**
* This many to one helper object is used on both arguments and filters.
*
......@@ -87,7 +89,7 @@ function add_table($join = NULL, $alias = NULL) {
// ensure_path logic. Perhaps it should be.
$r_join = clone $join;
while ($r_join->left_table != $base_table) {
$r_join = views_get_table_join($r_join->left_table, $base_table);
$r_join = HandlerBase::getTableJoin($r_join->left_table, $base_table);
}
// If we found that there are tables in between, add the relationship.
if ($r_join->table != $join->table) {
......
......@@ -10,6 +10,9 @@
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\views\Plugin\views\PluginBase;
use Drupal\views\ViewExecutable;
use Drupal\Core\Database\Database;
use DateTimeZone;
use DateTime;
abstract class HandlerBase extends PluginBase {
......@@ -593,7 +596,7 @@ public function getJoin() {
$base_table = $this->query->relationships[$this->relationship]['base'];
}
$join = views_get_table_join($this->table, $base_table);
$join = $this->getTableJoin($this->table, $base_table);
if ($join) {
return clone $join;
}
......@@ -618,4 +621,309 @@ public function validate() { return array(); }
*/
public function broken() { }
/**
* Creates cross-database SQL date formatting.
*
* @param string $format
* A format string for the result, like 'Y-m-d H:i:s'.
*
* @return string
* An appropriate SQL string for the DB type and field type.
*/
public function getSQLFormat($format) {
$db_type = Database::getConnection()->databaseType();
$field = $this->getSQLDateField();
switch ($db_type) {
case 'mysql':
$replace = array(
'Y' => '%Y',
'y' => '%y',
'M' => '%b',
'm' => '%m',
'n' => '%c',
'F' => '%M',
'D' => '%a',
'd' => '%d',
'l' => '%W',
'j' => '%e',
'W' => '%v',
'H' => '%H',
'h' => '%h',
'i' => '%i',
's' => '%s',
'A' => '%p',
);
$format = strtr($format, $replace);
return "DATE_FORMAT($field, '$format')";
case 'pgsql':
$replace = array(
'Y' => 'YYYY',
'y' => 'YY',
'M' => 'Mon',
'm' => 'MM',
'n' => 'MM', // no format for Numeric representation of a month, without leading zeros
'F' => 'Month',
'D' => 'Dy',
'd' => 'DD',
'l' => 'Day',
'j' => 'DD', // no format for Day of the month without leading zeros
'W' => 'WW',
'H' => 'HH24',
'h' => 'HH12',
'i' => 'MI',
's' => 'SS',
'A' => 'AM',
);
$format = strtr($format, $replace);
return "TO_CHAR($field, '$format')";
case 'sqlite':
$replace = array(
'Y' => '%Y', // 4 digit year number
'y' => '%Y', // no format for 2 digit year number
'M' => '%m', // no format for 3 letter month name
'm' => '%m', // month number with leading zeros
'n' => '%m', // no format for month number without leading zeros
'F' => '%m', // no format for full month name
'D' => '%d', // no format for 3 letter day name
'd' => '%d', // day of month number with leading zeros
'l' => '%d', // no format for full day name
'j' => '%d', // no format for day of month number without leading zeros
'W' => '%W', // ISO week number
'H' => '%H', // 24 hour hour with leading zeros
'h' => '%H', // no format for 12 hour hour with leading zeros
'i' => '%M', // minutes with leading zeros
's' => '%S', // seconds with leading zeros
'A' => '', // no format for AM/PM
);
$format = strtr($format, $replace);
return "strftime('$format', $field, 'unixepoch')";
}
}
/**
* Creates cross-database SQL dates.
*
* @return string
* An appropriate SQL string for the db type and field type.
*/
public function getSQLDateField() {
$field = "$this->tableAlias.$this->realField";
$db_type = Database::getConnection()->databaseType();
$offset = $this->getTimezone();
if (isset($offset) && !is_numeric($offset)) {
$dtz = new DateTimeZone($offset);
$dt = new DateTime('now', $dtz);
$offset_seconds = $dtz->getOffset($dt);
}
switch ($db_type) {
case 'mysql':
$field = "DATE_ADD('19700101', INTERVAL $field SECOND)";
if (!empty($offset)) {
$field = "($field + INTERVAL $offset_seconds SECOND)";
}
return $field;
case 'pgsql':
$field = "TO_TIMESTAMP($field)";
if (!empty($offset)) {
$field = "($field + INTERVAL '$offset_seconds SECONDS')";
}
return $field;
case 'sqlite':
if (!empty($offset)) {
$field = "($field + '$offset_seconds')";
}
return $field;
}
}
/**
* Figure out what timezone we're in; needed for some date manipulations.
*/
public static function getTimezone() {
global $user;
if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', 0);
}
// set up the database timezone
$db_type = Database::getConnection()->databaseType();
if (in_array($db_type, array('mysql', 'pgsql'))) {
$offset = '+00:00';
static $already_set = FALSE;
if (!$already_set) {
if ($db_type == 'pgsql') {
db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
}
elseif ($db_type == 'mysql') {
db_query("SET @@session.time_zone = '$offset'");
}
$already_set = TRUE;
}
}
return $timezone;
}
/**
* Fetches a handler to join one table to a primary table from the data cache.
*
* @param string $table
* The table to join from.
* @param string $base_table
* The table to join to.
*
* @return Drupal\views\Plugin\views\join\JoinPluginBase
*/
public static function getTableJoin($table, $base_table) {
$data = views_fetch_data($table);
if (isset($data['table']['join'][$base_table])) {
$h = $data['table']['join'][$base_table];
if (!empty($h['join_id']) && class_exists($h['handler'])) {
$id = $h['join_id'];
}
else {
$id = 'standard';
}
$handler = views_get_plugin('join', $id);
// Fill in some easy defaults
$handler->definition = $h;
if (empty($handler->definition['table'])) {
$handler->definition['table'] = $table;
}
// If this is empty, it's a direct link.
if (empty($handler->definition['left_table'])) {
$handler->definition['left_table'] = $base_table;
}
if (isset($h['arguments'])) {
call_user_func_array(array(&$handler, 'construct'), $h['arguments']);
}
else {
$handler->construct();
}
return $handler;
}
}
/**
* Breaks x,y,z and x+y+z into an array. Numeric only.
*
* @param string $str
* The string to parse.
* @param Drupal\views\Plugin\views\HandlerBase|null $handler
* The handler object to use as a base. If not specified one will
* be created.
*
* @return Drupal\views\Plugin\views\HandlerBase|stdClass $handler
* The new handler object.
*/
public static function breakPhrase($str, &$handler = NULL) {
if (!$handler) {
$handler = new \stdClass();
}
// Set up defaults:
if (!isset($handler->value)) {
$handler->value = array();
}
if (!isset($handler->operator)) {
$handler->operator = 'or';
}
if (empty($str)) {
return $handler;
}
if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) {
// The '+' character in a query string may be parsed as ' '.
$handler->operator = 'or';
$handler->value = preg_split('/[+ ]/', $str);
}
elseif (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) {
$handler->operator = 'and';
$handler->value = explode(',', $str);
}
// Keep an 'error' value if invalid strings were given.
if (!empty($str) && (empty($handler->value) || !is_array($handler->value))) {
$handler->value = array(-1);
return $handler;
}
// Doubly ensure that all values are numeric only.
foreach ($handler->value as $id => $value) {
$handler->value[$id] = intval($value);
}
return $handler;
}
/**
* Breaks x,y,z and x+y+z into an array. Works for strings.
*
* @param string $str
* The string to parse.
* @param Drupal\views\Plugin\views\HandlerBase|null $handler
* The object to use as a base. If not specified one will
* be created.
*
* @return Drupal\views\Plugin\views\HandlerBase|stdClass $handler
* The new handler object.
*/
public static function breakPhraseString($str, &$handler = NULL) {
if (!$handler) {
$handler = new \stdClass();
}
// Set up defaults:
if (!isset($handler->value)) {
$handler->value = array();
}
if (!isset($handler->operator)) {
$handler->operator = 'or';
}
if ($str == '') {
return $handler;
}
// Determine if the string has 'or' operators (plus signs) or 'and' operators
// (commas) and split the string accordingly. If we have an 'and' operator,
// spaces are treated as part of the word being split, but otherwise they are
// treated the same as a plus sign.
$or_wildcard = '[^\s+,]';
$and_wildcard = '[^+,]';
if (preg_match("/^({$or_wildcard}+[+ ])+{$or_wildcard}+$/", $str)) {
$handler->operator = 'or';
$handler->value = preg_split('/[+ ]/', $str);
}
elseif (preg_match("/^({$and_wildcard}+,)*{$and_wildcard}+$/", $str)) {
$handler->operator = 'and';
$handler->value = explode(',', $str);
}
// Keep an 'error' value if invalid strings were given.
if (!empty($str) && (empty($handler->value) || !is_array($handler->value))) {
$handler->value = array(-1);
return $handler;
}
// Doubly ensure that all values are strings only.
foreach ($handler->value as $id => $value) {
$handler->value[$id] = (string) $value;
}
return $handler;
}
}
......@@ -820,7 +820,7 @@ function summary_name_field() {
// if the alias is different then we're probably added, not ensured,
// so look up the join and add it instead.
if ($this->tableAlias != $this->name_table) {
$j = views_get_table_join($this->name_table, $this->table);
$j = HandlerBase::getTableJoin($this->name_table, $this->table);
if ($j) {
$join = clone $j;
$join->left_table = $this->tableAlias;
......
......@@ -83,4 +83,63 @@ function get_sort_name() {
return t('Date', array(), array('context' => 'Sort order'));
}
/**
* Creates cross-database SQL date extraction.
*
* @param string $extract_type
* The type of value to extract from the date, like 'MONTH'.
*
* @return string
* An appropriate SQL string for the DB type and field type.
*/
public function extractSQL($extract_type) {
$db_type = Database::getConnection()->databaseType();
$field = $this->getSQLDateField();
// Note there is no space after FROM to avoid db_rewrite problems
// see http://drupal.org/node/79904.
switch ($extract_type) {
case 'DATE':
return $field;
case 'YEAR':
return "EXTRACT(YEAR FROM($field))";
case 'MONTH':
return "EXTRACT(MONTH FROM($field))";
case 'DAY':
return "EXTRACT(DAY FROM($field))";
case 'HOUR':
return "EXTRACT(HOUR FROM($field))";
case 'MINUTE':
return "EXTRACT(MINUTE FROM($field))";
case 'SECOND':
return "EXTRACT(SECOND FROM($field))";
// ISO week number for date
case 'WEEK':
switch ($db_type) {
case 'mysql':
// WEEK using arg 3 in mysql should return the same value as postgres
// EXTRACT.
return "WEEK($field, 3)";
case 'pgsql':
return "EXTRACT(WEEK FROM($field))";
}
case 'DOW':
switch ($db_type) {
case 'mysql':
// mysql returns 1 for Sunday through 7 for Saturday php date
// functions and postgres use 0 for Sunday and 6 for Saturday.
return "INTEGER(DAYOFWEEK($field) - 1)";
case 'pgsql':
return "EXTRACT(DOW FROM($field))";
}
case 'DOY':
switch ($db_type) {
case 'mysql':
return "DAYOFYEAR($field)";
case 'pgsql':
return "EXTRACT(DOY FROM($field))";
}
}
}
}
......@@ -117,11 +117,11 @@ public function query($group_by = FALSE) {
}
if (!empty($this->options['break_phrase'])) {
if (!empty($this->definition['nummeric'])) {
views_break_phrase($this->argument, $this);
if (!empty($this->definition['numeric'])) {
$this->breakPhrase($this->argument, $this);
}
else {
views_break_phrase_string($this->argument, $this);
$this->breakPhraseString($this->argument, $this);
}
}
else {
......@@ -138,7 +138,7 @@ function title() {
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
$this->breakPhrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
......
......@@ -69,7 +69,7 @@ function title() {
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
$this->breakPhrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
......@@ -100,7 +100,7 @@ public function query($group_by = FALSE) {
$this->ensureMyTable();
if (!empty($this->options['break_phrase'])) {
views_break_phrase($this->argument, $this);
$this->breakPhrase($this->argument, $this);
}
else {
$this->value = array($this->argument);
......
......@@ -186,7 +186,7 @@ public function query($group_by = FALSE) {
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase_string($argument, $this);
$this->breakPhraseString($argument, $this);
}
else {
$this->value = array($argument);
......@@ -257,7 +257,7 @@ function title() {
}
if (!empty($this->options['break_phrase'])) {
views_break_phrase_string($this->argument, $this);
$this->breakPhraseString($this->argument, $this);
}
else {
$this->value = array($this->argument);
......
......@@ -10,6 +10,7 @@
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\views\Plugin\views\join\JoinPluginBase;
use Drupal\views\Plugin\views\HandlerBase;
use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
use Drupal\views\ViewExecutable;
......@@ -700,7 +701,7 @@ function get_join_data($table, $base_table) {
if (!empty($this->table_queue[$table])) {
$table = $this->table_queue[$table]['table'];
}
return views_get_table_join($table, $base_table);
return HandlerBase::getTableJoin($table, $base_table);
}
/**
......
......@@ -59,19 +59,19 @@ public function query() {
$this->query->add_orderby($this->tableAlias, $this->realField, $this->options['order']);
return;
case 'minute':
$formula = views_date_sql_format('YmdHi', "$this->tableAlias.$this->realField");
$formula = $this->getSQLFormat('YmdHi');
break;
case 'hour':
$formula = views_date_sql_format('YmdH', "$this->tableAlias.$this->realField");
$formula = $this->getSQLFormat('YmdH');
break;
case 'day':
$formula = views_date_sql_format('Ymd', "$this->tableAlias.$this->realField");
$formula = $this->getSQLFormat('Ymd');
break;
case 'month':
$formula = views_date_sql_format('Ym', "$this->tableAlias.$this->realField");
$formula = $this->getSQLFormat('Ym');
break;
case 'year':
$formula = views_date_sql_format('Y', "$this->tableAlias.$this->realField");
$formula = $this->getSQLFormat('Y');
break;
}
......
......@@ -9,6 +9,7 @@
use stdClass;
use Drupal\views\Tests\ViewTestBase;
use Drupal\views\Plugin\views\HandlerBase;
/**
* Tests abstract handlers of views.
......@@ -54,58 +55,56 @@ function testFilterInOperatorUi() {
}
/**
* Tests views_break_phrase_string function.
* Tests the breakPhraseString() method.
*/
function test_views_break_phrase_string() {
function testBreakPhraseString() {
$empty_stdclass = new StdClass();
$empty_stdclass->operator = 'or';
$empty_stdclass->value = array();
views_include('handlers');
$null = NULL;
// check defaults
$this->assertEqual($empty_stdclass, views_break_phrase_string('', $null));
$null = NULL;
$this->assertEqual($empty_stdclass, HandlerBase::breakPhraseString('', $null));
$handler = views_get_handler('node', 'title', 'argument');
$this->assertEqual($handler, views_break_phrase_string('', $handler), 'The views_break_phrase_string() works correctly.');
$this->assertEqual($handler, HandlerBase::breakPhraseString('', $handler), 'The breakPhraseString() method works correctly.');
// test ors
$handler = views_break_phrase_string('word1 word2+word');
$handler = HandlerBase::breakPhraseString('word1 word2+word');
$this->assertEqualValue(array('word1', 'word2', 'word'), $handler);
$this->assertEqual('or', $handler->operator);
$handler = views_break_phrase_string('word1+word2+word');
$handler = HandlerBase::breakPhraseString('word1+word2+word');
$this->assertEqualValue(array('word1', 'word2', 'word'), $handler);
$this->assertEqual('or', $handler->operator);
$handler = views_break_phrase_string('word1 word2 word');
$handler = HandlerBase::breakPhraseString('word1 word2 word');
$this->assertEqualValue(array('word1', 'word2', 'word'), $handler);
$this->assertEqual('or', $handler->operator);
$handler = views_break_phrase_string('word-1+word-2+word');
$handler = HandlerBase::breakPhraseString('word-1+word-2+word');
$this->assertEqualValue(array('word-1', 'word-2', 'word'), $handler);
$this->assertEqual('or', $handler->operator);
$handler = views_break_phrase_string('wõrd1+wõrd2+wõrd');
$handler = HandlerBase::breakPhraseString('wõrd1+wõrd2+wõrd');
$this->assertEqualValue(array('wõrd1', 'wõrd2', 'wõrd'), $handler);
$this->assertEqual('or', $handler->operator);
// test ands.
$handler = views_break_phrase_string('word1,word2,word');
$handler = HandlerBase::breakPhraseString('word1,word2,word');
$this->assertEqualValue(array('word1', 'word2', 'word'), $handler);
$this->assertEqual('and', $handler->operator);
$handler = views_break_phrase_string('word1 word2,word');
$handler = HandlerBase::breakPhraseString('word1 word2,word');
$this->assertEqualValue(array('word1 word2', 'word'), $handler);
$this->assertEqual('and', $handler->operator);
$handler = views_break_phrase_string('word1,word2 word');
$handler = HandlerBase::breakPhraseString('word1,word2 word');
$this->assertEqualValue(array('word1', 'word2 word'), $handler);
$this->assertEqual('and', $handler->operator);
$handler = views_break_phrase_string('word-1,word-2,word');
$handler = HandlerBase::breakPhraseString('word-1,word-2,word');
$this->assertEqualValue(array('word-1', 'word-2', 'word'), $handler);
$this->assertEqual('and', $handler->operator);
$handler = views_break_phrase_string('wõrd1,wõrd2,wõrd');
$handler = HandlerBase::breakPhraseString('wõrd1,wõrd2,wõrd');
$this->assertEqualValue(array('wõrd1', 'wõrd2', 'wõrd'), $handler);
$this->assertEqual('and', $handler->operator);
// test a single word
$handler = views_break_phrase_string('word');
$handler = HandlerBase::breakPhraseString('word');
$this->assertEqualValue(array('word'), $handler);
$this->assertEqual('and', $handler->operator);
}
......@@ -113,36 +112,36 @@ function test_views_break_phrase_string() {
/**
* Tests views_break_phrase function.
*/
function test_views_break_phrase() {
function testBreakPhrase() {
$empty_stdclass = new StdClass();
$empty_stdclass->operator = 'or';