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;
54 public function operatorOptions($which = 'title') {
56 foreach ($this->operators() as $id => $info) {
57 $options[$id] = $info[$which];
64 * Returns an array of operator information.
68 protected function operators() {
71 'title' => $this->t('Is equal to'),
72 'method' => 'queryOpBoolean',
73 'short' => $this->t('='),
75 'query_operator' => self::EQUAL,
78 'title' => $this->t('Is not equal to'),
79 'method' => 'queryOpBoolean',
80 'short' => $this->t('!='),
82 'query_operator' => self::NOT_EQUAL,
90 public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
91 parent::init($view, $display, $options);
93 $this->value_value = $this->t('True');
95 if (isset($this->definition['label'])) {
96 $this->value_value = $this->definition['label'];
98 elseif (isset($this->definition['title'])) {
99 $this->value_value = $this->definition['title'];
102 if (isset($this->definition['accept null'])) {
103 $this->accept_null = (bool) $this->definition['accept null'];
105 elseif (isset($this->definition['accept_null'])) {
106 $this->accept_null = (bool) $this->definition['accept_null'];
108 $this->valueOptions = NULL;
112 * Return the possible options for this filter.
114 * Child classes should override this function to set the possible values
115 * for the filter. Since this is a boolean filter, the array should have
116 * two possible keys: 1 for "True" and 0 for "False", although the labels
117 * can be whatever makes sense for the filter. These values are used for
118 * configuring the filter, when the filter is exposed, and in the admin
119 * summary of the filter. Normally, this should be static data, but if it's
120 * dynamic for some reason, child classes should use a guard to reduce
121 * database hits as much as possible.
123 public function getValueOptions() {
124 if (isset($this->definition['type'])) {
125 if ($this->definition['type'] == 'yes-no') {
126 $this->valueOptions = [1 => $this->t('Yes'), 0 => $this->t('No')];
128 if ($this->definition['type'] == 'on-off') {
129 $this->valueOptions = [1 => $this->t('On'), 0 => $this->t('Off')];
131 if ($this->definition['type'] == 'enabled-disabled') {
132 $this->valueOptions = [1 => $this->t('Enabled'), 0 => $this->t('Disabled')];
136 // Provide a fallback if the above didn't set anything.
137 if (!isset($this->valueOptions)) {
138 $this->valueOptions = [1 => $this->t('True'), 0 => $this->t('False')];
142 protected function defineOptions() {
143 $options = parent::defineOptions();
145 $options['value']['default'] = FALSE;
150 protected function valueForm(&$form, FormStateInterface $form_state) {
151 if (empty($this->valueOptions)) {
152 // Initialize the array of possible values for this filter.
153 $this->getValueOptions();
155 if ($exposed = $form_state->get('exposed')) {
156 // Exposed filter: use a select box to save space.
157 $filter_form_type = 'select';
160 // Configuring a filter: use radios for clarity.
161 $filter_form_type = 'radios';
164 '#type' => $filter_form_type,
165 '#title' => $this->value_value,
166 '#options' => $this->valueOptions,
167 '#default_value' => $this->value,
169 if (!empty($this->options['exposed'])) {
170 $identifier = $this->options['expose']['identifier'];
171 $user_input = $form_state->getUserInput();
172 if ($exposed && !isset($user_input[$identifier])) {
173 $user_input[$identifier] = $this->value;
174 $form_state->setUserInput($user_input);
176 // If we're configuring an exposed filter, add an - Any - option.
177 if (!$exposed || empty($this->options['expose']['required'])) {
178 $form['value']['#options'] = ['All' => $this->t('- Any -')] + $form['value']['#options'];
183 protected function valueValidate($form, FormStateInterface $form_state) {
184 if ($form_state->getValue(['options', 'value']) == 'All' && !$form_state->isValueEmpty(['options', 'expose', 'required'])) {
185 $form_state->setErrorByName('value', $this->t('You must select a value unless this is an non-required exposed filter.'));
189 public function adminSummary() {
190 if ($this->isAGroup()) {
191 return $this->t('grouped');
193 if (!empty($this->options['exposed'])) {
194 return $this->t('exposed');
196 if (empty($this->valueOptions)) {
197 $this->getValueOptions();
199 // Now that we have the valid options for this filter, just return the
200 // human-readable label based on the current value. The valueOptions
201 // array is keyed with either 0 or 1, so if the current value is not
202 // empty, use the label for 1, and if it's empty, use the label for 0.
203 return $this->operator . ' ' . $this->valueOptions[!empty($this->value)];
206 public function defaultExposeOptions() {
207 parent::defaultExposeOptions();
208 $this->options['expose']['operator_id'] = '';
209 $this->options['expose']['label'] = $this->value_value;
210 $this->options['expose']['required'] = TRUE;
216 public function query() {
217 $this->ensureMyTable();
218 $field = "$this->tableAlias.$this->realField";
220 $info = $this->operators();
221 if (!empty($info[$this->operator]['method'])) {
222 call_user_func([$this, $info[$this->operator]['method']], $field, $info[$this->operator]['query_operator']);
227 * Adds a where condition to the query for a boolean value.
229 * @param string $field
230 * The field name to add the where condition for.
231 * @param string $query_operator
232 * (optional) Either self::EQUAL or self::NOT_EQUAL. Defaults to
235 protected function queryOpBoolean($field, $query_operator = self::EQUAL) {
236 if (empty($this->value)) {
237 if ($this->accept_null) {
238 if ($query_operator === self::EQUAL) {
239 $condition = (new Condition('OR'))
240 ->condition($field, 0, $query_operator)
244 $condition = (new Condition('AND'))
245 ->condition($field, 0, $query_operator)
248 $this->query->addWhere($this->options['group'], $condition);
251 $this->query->addWhere($this->options['group'], $field, 0, $query_operator);
255 if (!empty($this->definition['use_equal'])) {
256 // Forces a self::EQUAL operator instead of a self::NOT_EQUAL for
257 // performance reasons.
258 if ($query_operator === self::EQUAL) {
259 $this->query->addWhere($this->options['group'], $field, 1, self::EQUAL);
262 $this->query->addWhere($this->options['group'], $field, 0, self::EQUAL);
266 $this->query->addWhere($this->options['group'], $field, 1, $query_operator);