views_handler_filter_string.inc

  1. 3.x handlers/views_handler_filter_string.inc
  2. 2.x handlers/views_handler_filter_string.inc

File

handlers/views_handler_filter_string.inc
View source
  1. <?php
  2. /**
  3. * Basic textfield filter to handle string filtering commands
  4. * including equality, like, not like, etc.
  5. */
  6. class views_handler_filter_string extends views_handler_filter {
  7. // exposed filter options
  8. var $no_single = TRUE;
  9. var $no_optional = TRUE;
  10. function option_definition() {
  11. $options = parent::option_definition();
  12. $options['expose']['contains']['optional'] = array('default' => FALSE);
  13. $options['case'] = array('default' => TRUE);
  14. return $options;
  15. }
  16. /**
  17. * This kind of construct makes it relatively easy for a child class
  18. * to add or remove functionality by overriding this function and
  19. * adding/removing items from this array.
  20. */
  21. function operators() {
  22. $operators = array(
  23. '=' => array(
  24. 'title' => t('Is equal to'),
  25. 'short' => t('='),
  26. 'method' => 'op_equal',
  27. 'values' => 1,
  28. ),
  29. '!=' => array(
  30. 'title' => t('Is not equal to'),
  31. 'short' => t('!='),
  32. 'method' => 'op_equal',
  33. 'values' => 1,
  34. ),
  35. 'contains' => array(
  36. 'title' => t('Contains'),
  37. 'short' => t('contains'),
  38. 'method' => 'op_contains',
  39. 'values' => 1,
  40. ),
  41. 'word' => array(
  42. 'title' => t('Contains any word'),
  43. 'short' => t('has word'),
  44. 'method' => 'op_word',
  45. 'values' => 1,
  46. ),
  47. 'allwords' => array(
  48. 'title' => t('Contains all words'),
  49. 'short' => t('has all'),
  50. 'method' => 'op_word',
  51. 'values' => 1,
  52. ),
  53. 'starts' => array(
  54. 'title' => t('Starts with'),
  55. 'short' => t('begins'),
  56. 'method' => 'op_starts',
  57. 'values' => 1,
  58. ),
  59. 'not_starts' => array(
  60. 'title' => t('Does not start with'),
  61. 'short' => t('not_begins'),
  62. 'method' => 'op_not_starts',
  63. 'values' => 1,
  64. ),
  65. 'ends' => array(
  66. 'title' => t('Ends with'),
  67. 'short' => t('ends'),
  68. 'method' => 'op_ends',
  69. 'values' => 1,
  70. ),
  71. 'not_ends' => array(
  72. 'title' => t('Does not end with'),
  73. 'short' => t('not_ends'),
  74. 'method' => 'op_not_ends',
  75. 'values' => 1,
  76. ),
  77. 'not' => array(
  78. 'title' => t('Does not contain'),
  79. 'short' => t('!has'),
  80. 'method' => 'op_not',
  81. 'values' => 1,
  82. ),
  83. );
  84. // if the definition allows for the empty operator, add it.
  85. if (!empty($this->definition['allow empty'])) {
  86. $operators += array(
  87. 'empty' => array(
  88. 'title' => t('Is empty (NULL)'),
  89. 'method' => 'op_empty',
  90. 'short' => t('empty'),
  91. 'values' => 0,
  92. ),
  93. 'not empty' => array(
  94. 'title' => t('Is not empty (NOT NULL)'),
  95. 'method' => 'op_empty',
  96. 'short' => t('not empty'),
  97. 'values' => 0,
  98. ),
  99. );
  100. }
  101. return $operators;
  102. }
  103. /**
  104. * Build strings from the operators() for 'select' options
  105. */
  106. function operator_options($which = 'title') {
  107. $options = array();
  108. foreach ($this->operators() as $id => $info) {
  109. $options[$id] = $info[$which];
  110. }
  111. return $options;
  112. }
  113. function admin_summary() {
  114. if (!empty($this->options['exposed'])) {
  115. return t('exposed');
  116. }
  117. $options = $this->operator_options('short');
  118. $output = check_plain($options[$this->operator]);
  119. if (in_array($this->operator, $this->operator_values(1))) {
  120. $output .= ' ' . check_plain($this->value);
  121. }
  122. return $output;
  123. }
  124. function options_form(&$form, &$form_state) {
  125. parent::options_form($form, $form_state);
  126. $form['case'] = array(
  127. '#type' => 'checkbox',
  128. '#title' => t('Case sensitive'),
  129. '#default_value' => $this->options['case'],
  130. '#description' => t('Case sensitive filters may be faster. MySQL might ignore case sensitivity.'),
  131. );
  132. }
  133. function operator_values($values = 1) {
  134. $options = array();
  135. foreach ($this->operators() as $id => $info) {
  136. if (isset($info['values']) && $info['values'] == $values) {
  137. $options[] = $id;
  138. }
  139. }
  140. return $options;
  141. }
  142. /**
  143. * Provide a simple textfield for equality
  144. */
  145. function value_form(&$form, &$form_state) {
  146. // We have to make some choices when creating this as an exposed
  147. // filter form. For example, if the operator is locked and thus
  148. // not rendered, we can't render dependencies; instead we only
  149. // render the form items we need.
  150. $which = 'all';
  151. if (!empty($form['operator'])) {
  152. $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
  153. }
  154. if (!empty($form_state['exposed'])) {
  155. $identifier = $this->options['expose']['identifier'];
  156. if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator'])) {
  157. // exposed and locked.
  158. $which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none';
  159. }
  160. else {
  161. $source = 'edit-' . form_clean_id($this->options['expose']['operator']);
  162. }
  163. }
  164. if ($which == 'all' || $which == 'value') {
  165. $form['value'] = array(
  166. '#type' => 'textfield',
  167. '#title' => t('Value'),
  168. '#size' => 30,
  169. '#default_value' => $this->value,
  170. );
  171. if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
  172. $form_state['input'][$identifier] = $this->value;
  173. }
  174. if ($which == 'all') {
  175. $form['value'] += array(
  176. '#process' => array('views_process_dependency'),
  177. '#dependency' => array($source => $this->operator_values(1)),
  178. );
  179. }
  180. }
  181. if (!isset($form['value'])) {
  182. // Ensure there is something in the 'value'.
  183. $form['value'] = array(
  184. '#type' => 'value',
  185. '#value' => NULL
  186. );
  187. }
  188. }
  189. function case_transform() {
  190. return !empty($this->options['case']) ? '' : 'UPPER';
  191. }
  192. /**
  193. * Add this filter to the query.
  194. *
  195. * Due to the nature of fapi, the value and the operator have an unintended
  196. * level of indirection. You will find them in $this->operator
  197. * and $this->value respectively.
  198. */
  199. function query() {
  200. $this->ensure_my_table();
  201. $field = "$this->table_alias.$this->real_field";
  202. $upper = $this->case_transform();
  203. $info = $this->operators();
  204. if (!empty($info[$this->operator]['method'])) {
  205. $this->{$info[$this->operator]['method']}($field, $upper);
  206. }
  207. }
  208. function op_equal($field, $upper) {
  209. // operator is either = or !=
  210. $this->query->add_where($this->options['group'], "$upper($field) $this->operator $upper('%s')", $this->value);
  211. }
  212. function op_contains($field, $upper) {
  213. $this->query->add_where($this->options['group'], "$upper($field) LIKE $upper('%%%s%%')", $this->value);
  214. }
  215. function op_word($field, $upper) {
  216. $where = array();
  217. preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER);
  218. foreach ($matches as $match) {
  219. $phrase = false;
  220. // Strip off phrase quotes
  221. if ($match[2]{0} == '"') {
  222. $match[2] = substr($match[2], 1, -1);
  223. $phrase = true;
  224. }
  225. $words = trim($match[2], ',?!();:-');
  226. $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
  227. foreach ($words as $word) {
  228. $where[] = "$upper($field) LIKE $upper('%%%s%%')";
  229. $values[] = trim($word, " ,!?");
  230. }
  231. }
  232. if (!$where) {
  233. return;
  234. }
  235. if ($this->operator == 'word') {
  236. $where = '(' . implode(' OR ', $where) . ')';
  237. }
  238. else {
  239. $where = implode(' AND ', $where);
  240. }
  241. // previously this was a call_user_func_array but that's unnecessary
  242. // as views will unpack an array that is a single arg.
  243. $this->query->add_where($this->options['group'], $where, $values);
  244. }
  245. function op_starts($field, $upper) {
  246. $this->query->add_where($this->options['group'], "$upper($field) LIKE $upper('%s%%')", $this->value);
  247. }
  248. function op_not_starts($field, $upper) {
  249. $this->query->add_where($this->options['group'], "$upper($field) NOT LIKE $upper('%s%%')", $this->value);
  250. }
  251. function op_ends($field, $upper) {
  252. $this->query->add_where($this->options['group'], "$upper($field) LIKE $upper('%%%s')", $this->value);
  253. }
  254. function op_not_ends($field, $upper) {
  255. $this->query->add_where($this->options['group'], "$upper($field) NOT LIKE $upper('%%%s')", $this->value);
  256. }
  257. function op_not($field, $upper) {
  258. $this->query->add_where($this->options['group'], "$upper($field) NOT LIKE $upper('%%%s%%')", $this->value);
  259. }
  260. function op_empty($field) {
  261. if ($this->operator == 'empty') {
  262. $operator = "IS NULL";
  263. }
  264. else {
  265. $operator = "IS NOT NULL";
  266. }
  267. $this->query->add_where($this->options['group'], "$field $operator");
  268. }
  269. }