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