3 namespace Drupal\views\Plugin\views\filter;
5 use Drupal\Core\Database\Query\Condition;
6 use Drupal\Core\Form\FormStateInterface;
7 use Drupal\views\Plugin\views\display\DisplayPluginBase;
8 use Drupal\views\ViewExecutable;
11 * Simple filter to handle matching of boolean values
14 * - label: (REQUIRED) The label for the checkbox.
15 * - type: For basic 'true false' types, an item can specify the following:
16 * - true-false: True/false (this is the default)
19 * - enabled-disabled: Enabled/Disabled
20 * - accept null: Treat a NULL value as false.
21 * - use_equal: If you use this flag the query will use = 1 instead of <> 0.
22 * This might be helpful for performance reasons.
24 * @ingroup views_filter_handlers
26 * @ViewsFilter("boolean")
28 class BooleanOperator extends FilterPluginBase {
31 * The equal query operator.
38 * The non equal query operator.
42 const NOT_EQUAL = '<>';
44 // exposed filter options
45 protected $alwaysMultiple = TRUE;
46 // Don't display empty space where the operator would be.
47 public $no_operator = TRUE;
48 // Whether to accept NULL as a false value or not
49 public $accept_null = FALSE;
55 public function operatorOptions($which = 'title') {
57 foreach ($this->operators() as $id => $info) {
58 $options[$id] = $info[$which];
65 * Returns an array of operator information.
69 protected function operators() {
72 'title' => $this->t('Is equal to'),
73 'method' => 'queryOpBoolean',
74 'short' => $this->t('='),
76 'query_operator' => self::EQUAL,
79 'title' => $this->t('Is not equal to'),
80 'method' => 'queryOpBoolean',
81 'short' => $this->t('!='),
83 'query_operator' => self::NOT_EQUAL,
91 public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
92 parent::init($view, $display, $options);
94 $this->value_value = $this->t('True');
96 if (isset($this->definition['label'])) {
97 $this->value_value = $this->definition['label'];
99 elseif (isset($this->definition['title'])) {
100 $this->value_value = $this->definition['title'];
103 if (isset($this->definition['accept null'])) {
104 $this->accept_null = (bool) $this->definition['accept null'];
106 elseif (isset($this->definition['accept_null'])) {
107 $this->accept_null = (bool) $this->definition['accept_null'];
109 $this->valueOptions = NULL;
113 * Return the possible options for this filter.
115 * Child classes should override this function to set the possible values
116 * for the filter. Since this is a boolean filter, the array should have
117 * two possible keys: 1 for "True" and 0 for "False", although the labels
118 * can be whatever makes sense for the filter. These values are used for
119 * configuring the filter, when the filter is exposed, and in the admin
120 * summary of the filter. Normally, this should be static data, but if it's
121 * dynamic for some reason, child classes should use a guard to reduce
122 * database hits as much as possible.
124 public function getValueOptions() {
125 if (isset($this->definition['type'])) {
126 if ($this->definition['type'] == 'yes-no') {
127 $this->valueOptions = [1 => $this->t('Yes'), 0 => $this->t('No')];
129 if ($this->definition['type'] == 'on-off') {
130 $this->valueOptions = [1 => $this->t('On'), 0 => $this->t('Off')];
132 if ($this->definition['type'] == 'enabled-disabled') {
133 $this->valueOptions = [1 => $this->t('Enabled'), 0 => $this->t('Disabled')];
137 // Provide a fallback if the above didn't set anything.
138 if (!isset($this->valueOptions)) {
139 $this->valueOptions = [1 => $this->t('True'), 0 => $this->t('False')];
143 protected function defineOptions() {
144 $options = parent::defineOptions();
146 $options['value']['default'] = FALSE;
151 protected function valueForm(&$form, FormStateInterface $form_state) {
152 if (empty($this->valueOptions)) {
153 // Initialize the array of possible values for this filter.
154 $this->getValueOptions();
156 if ($exposed = $form_state->get('exposed')) {
157 // Exposed filter: use a select box to save space.
158 $filter_form_type = 'select';
161 // Configuring a filter: use radios for clarity.
162 $filter_form_type = 'radios';
165 '#type' => $filter_form_type,
166 '#title' => $this->value_value,
167 '#options' => $this->valueOptions,
168 '#default_value' => $this->value,
170 if (!empty($this->options['exposed'])) {
171 $identifier = $this->options['expose']['identifier'];
172 $user_input = $form_state->getUserInput();
173 if ($exposed && !isset($user_input[$identifier])) {
174 $user_input[$identifier] = $this->value;
175 $form_state->setUserInput($user_input);
177 // If we're configuring an exposed filter, add an - Any - option.
178 if (!$exposed || empty($this->options['expose']['required'])) {
179 $form['value']['#options'] = ['All' => $this->t('- Any -')] + $form['value']['#options'];
184 protected function valueValidate($form, FormStateInterface $form_state) {
185 if ($form_state->getValue(['options', 'value']) == 'All' && !$form_state->isValueEmpty(['options', 'expose', 'required'])) {
186 $form_state->setErrorByName('value', $this->t('You must select a value unless this is an non-required exposed filter.'));
190 public function adminSummary() {
191 if ($this->isAGroup()) {
192 return $this->t('grouped');
194 if (!empty($this->options['exposed'])) {
195 return $this->t('exposed');
197 if (empty($this->valueOptions)) {
198 $this->getValueOptions();
200 // Now that we have the valid options for this filter, just return the
201 // human-readable label based on the current value. The valueOptions
202 // array is keyed with either 0 or 1, so if the current value is not
203 // empty, use the label for 1, and if it's empty, use the label for 0.
204 return $this->operator . ' ' . $this->valueOptions[!empty($this->value)];
207 public function defaultExposeOptions() {
208 parent::defaultExposeOptions();
209 $this->options['expose']['operator_id'] = '';
210 $this->options['expose']['label'] = $this->value_value;
211 $this->options['expose']['required'] = TRUE;
217 public function query() {
218 $this->ensureMyTable();
219 $field = "$this->tableAlias.$this->realField";
221 $info = $this->operators();
222 if (!empty($info[$this->operator]['method'])) {
223 call_user_func([$this, $info[$this->operator]['method']], $field, $info[$this->operator]['query_operator']);
228 * Adds a where condition to the query for a boolean value.
230 * @param string $field
231 * The field name to add the where condition for.
232 * @param string $query_operator
233 * (optional) Either self::EQUAL or self::NOT_EQUAL. Defaults to
236 protected function queryOpBoolean($field, $query_operator = self::EQUAL) {
237 if (empty($this->value)) {
238 if ($this->accept_null) {
239 if ($query_operator === self::EQUAL) {
240 $condition = (new Condition('OR'))
241 ->condition($field, 0, $query_operator)
245 $condition = (new Condition('AND'))
246 ->condition($field, 0, $query_operator)
249 $this->query->addWhere($this->options['group'], $condition);
252 $this->query->addWhere($this->options['group'], $field, 0, $query_operator);
256 if (!empty($this->definition['use_equal'])) {
257 // Forces a self::EQUAL operator instead of a self::NOT_EQUAL for
258 // performance reasons.
259 if ($query_operator === self::EQUAL) {
260 $this->query->addWhere($this->options['group'], $field, 1, self::EQUAL);
263 $this->query->addWhere($this->options['group'], $field, 0, self::EQUAL);
267 $this->query->addWhere($this->options['group'], $field, 1, $query_operator);