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;
56 public function operatorOptions($which = 'title') {
58 foreach ($this->operators() as $id => $info) {
59 $options[$id] = $info[$which];
66 * Returns an array of operator information.
70 protected function operators() {
73 'title' => $this->t('Is equal to'),
74 'method' => 'queryOpBoolean',
75 'short' => $this->t('='),
77 'query_operator' => self::EQUAL,
80 'title' => $this->t('Is not equal to'),
81 'method' => 'queryOpBoolean',
82 'short' => $this->t('!='),
84 'query_operator' => self::NOT_EQUAL,
92 public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
93 parent::init($view, $display, $options);
95 $this->value_value = $this->t('True');
97 if (isset($this->definition['label'])) {
98 $this->value_value = $this->definition['label'];
100 elseif (isset($this->definition['title'])) {
101 $this->value_value = $this->definition['title'];
104 if (isset($this->definition['accept null'])) {
105 $this->accept_null = (bool) $this->definition['accept null'];
107 elseif (isset($this->definition['accept_null'])) {
108 $this->accept_null = (bool) $this->definition['accept_null'];
110 $this->valueOptions = NULL;
114 * Return the possible options for this filter.
116 * Child classes should override this function to set the possible values
117 * for the filter. Since this is a boolean filter, the array should have
118 * two possible keys: 1 for "True" and 0 for "False", although the labels
119 * can be whatever makes sense for the filter. These values are used for
120 * configuring the filter, when the filter is exposed, and in the admin
121 * summary of the filter. Normally, this should be static data, but if it's
122 * dynamic for some reason, child classes should use a guard to reduce
123 * database hits as much as possible.
125 public function getValueOptions() {
126 if (isset($this->definition['type'])) {
127 if ($this->definition['type'] == 'yes-no') {
128 $this->valueOptions = [1 => $this->t('Yes'), 0 => $this->t('No')];
130 if ($this->definition['type'] == 'on-off') {
131 $this->valueOptions = [1 => $this->t('On'), 0 => $this->t('Off')];
133 if ($this->definition['type'] == 'enabled-disabled') {
134 $this->valueOptions = [1 => $this->t('Enabled'), 0 => $this->t('Disabled')];
138 // Provide a fallback if the above didn't set anything.
139 if (!isset($this->valueOptions)) {
140 $this->valueOptions = [1 => $this->t('True'), 0 => $this->t('False')];
144 protected function defineOptions() {
145 $options = parent::defineOptions();
147 $options['value']['default'] = FALSE;
152 protected function valueForm(&$form, FormStateInterface $form_state) {
153 if (empty($this->valueOptions)) {
154 // Initialize the array of possible values for this filter.
155 $this->getValueOptions();
157 if ($exposed = $form_state->get('exposed')) {
158 // Exposed filter: use a select box to save space.
159 $filter_form_type = 'select';
162 // Configuring a filter: use radios for clarity.
163 $filter_form_type = 'radios';
166 '#type' => $filter_form_type,
167 '#title' => $this->value_value,
168 '#options' => $this->valueOptions,
169 '#default_value' => $this->value,
171 if (!empty($this->options['exposed'])) {
172 $identifier = $this->options['expose']['identifier'];
173 $user_input = $form_state->getUserInput();
174 if ($exposed && !isset($user_input[$identifier])) {
175 $user_input[$identifier] = $this->value;
176 $form_state->setUserInput($user_input);
178 // If we're configuring an exposed filter, add an - Any - option.
179 if (!$exposed || empty($this->options['expose']['required'])) {
180 $form['value']['#options'] = ['All' => $this->t('- Any -')] + $form['value']['#options'];
185 protected function valueValidate($form, FormStateInterface $form_state) {
186 if ($form_state->getValue(['options', 'value']) == 'All' && !$form_state->isValueEmpty(['options', 'expose', 'required'])) {
187 $form_state->setErrorByName('value', $this->t('You must select a value unless this is an non-required exposed filter.'));
191 public function adminSummary() {
192 if ($this->isAGroup()) {
193 return $this->t('grouped');
195 if (!empty($this->options['exposed'])) {
196 return $this->t('exposed');
198 if (empty($this->valueOptions)) {
199 $this->getValueOptions();
201 // Now that we have the valid options for this filter, just return the
202 // human-readable label based on the current value. The valueOptions
203 // array is keyed with either 0 or 1, so if the current value is not
204 // empty, use the label for 1, and if it's empty, use the label for 0.
205 return $this->operator . ' ' . $this->valueOptions[!empty($this->value)];
208 public function defaultExposeOptions() {
209 parent::defaultExposeOptions();
210 $this->options['expose']['operator_id'] = '';
211 $this->options['expose']['label'] = $this->value_value;
212 $this->options['expose']['required'] = TRUE;
218 public function query() {
219 $this->ensureMyTable();
220 $field = "$this->tableAlias.$this->realField";
222 $info = $this->operators();
223 if (!empty($info[$this->operator]['method'])) {
224 call_user_func([$this, $info[$this->operator]['method']], $field, $info[$this->operator]['query_operator']);
229 * Adds a where condition to the query for a boolean value.
231 * @param string $field
232 * The field name to add the where condition for.
233 * @param string $query_operator
234 * (optional) Either self::EQUAL or self::NOT_EQUAL. Defaults to
237 protected function queryOpBoolean($field, $query_operator = self::EQUAL) {
238 if (empty($this->value)) {
239 if ($this->accept_null) {
240 if ($query_operator === self::EQUAL) {
241 $condition = (new Condition('OR'))
242 ->condition($field, 0, $query_operator)
246 $condition = (new Condition('AND'))
247 ->condition($field, 0, $query_operator)
250 $this->query->addWhere($this->options['group'], $condition);
253 $this->query->addWhere($this->options['group'], $field, 0, $query_operator);
257 if (!empty($this->definition['use_equal'])) {
258 // Forces a self::EQUAL operator instead of a self::NOT_EQUAL for
259 // performance reasons.
260 if ($query_operator === self::EQUAL) {
261 $this->query->addWhere($this->options['group'], $field, 1, self::EQUAL);
264 $this->query->addWhere($this->options['group'], $field, 0, self::EQUAL);
268 $this->query->addWhere($this->options['group'], $field, 1, $query_operator);