3 namespace Drupal\views\Plugin\views\sort;
5 use Drupal\Core\Cache\Cache;
6 use Drupal\Core\Cache\CacheableDependencyInterface;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\views\Plugin\views\HandlerBase;
11 * @defgroup views_sort_handlers Views sort handler plugins
13 * Plugins that handle sorting for Views.
15 * Sort handlers extend \Drupal\views\Plugin\views\sort:SortPluginBase. They
16 * must be annotated with \Drupal\views\Annotation\ViewsSort annotation, and
17 * they must be in plugin directory Plugin\views\sort.
19 * @ingroup views_plugins
24 * Base sort handler that has no options and performs a simple sort.
26 abstract class SortPluginBase extends HandlerBase implements CacheableDependencyInterface {
29 * Determine if a sort can be exposed.
31 public function canExpose() {
36 * Called to add the sort to a query.
38 public function query() {
39 $this->ensureMyTable();
41 $this->query->addOrderBy($this->tableAlias, $this->realField, $this->options['order']);
44 protected function defineOptions() {
45 $options = parent::defineOptions();
47 $options['order'] = ['default' => 'ASC'];
48 $options['exposed'] = ['default' => FALSE];
49 $options['expose'] = [
51 'label' => ['default' => ''],
58 * Display whether or not the sort order is ascending or descending
60 public function adminSummary() {
61 if (!empty($this->options['exposed'])) {
62 return $this->t('Exposed');
64 switch ($this->options['order']) {
68 return $this->t('asc');
72 return $this->t('desc');
77 * Basic options for all sort criteria
79 public function buildOptionsForm(&$form, FormStateInterface $form_state) {
80 parent::buildOptionsForm($form, $form_state);
81 if ($this->canExpose()) {
82 $this->showExposeButton($form, $form_state);
84 $form['op_val_start'] = ['#value' => '<div class="clearfix">'];
85 $this->showSortForm($form, $form_state);
86 $form['op_val_end'] = ['#value' => '</div>'];
87 if ($this->canExpose()) {
88 $this->showExposeForm($form, $form_state);
93 * Shortcut to display the expose/hide button.
95 public function showExposeButton(&$form, FormStateInterface $form_state) {
96 $form['expose_button'] = [
97 '#prefix' => '<div class="views-expose clearfix">',
98 '#suffix' => '</div>',
99 // Should always come first
103 // Add a checkbox for JS users, which will have behavior attached to it
104 // so it can replace the button.
105 $form['expose_button']['checkbox'] = [
106 '#theme_wrappers' => ['container'],
107 '#attributes' => ['class' => ['js-only']],
109 $form['expose_button']['checkbox']['checkbox'] = [
110 '#title' => $this->t('Expose this sort to visitors, to allow them to change it'),
111 '#type' => 'checkbox',
114 // Then add the button itself.
115 if (empty($this->options['exposed'])) {
116 $form['expose_button']['markup'] = [
117 '#markup' => '<div class="description exposed-description" style="float: left; margin-right:10px">' . $this->t('This sort is not exposed. Expose it to allow the users to change it.') . '</div>',
119 $form['expose_button']['button'] = [
120 '#limit_validation_errors' => [],
122 '#value' => $this->t('Expose sort'),
123 '#submit' => [[$this, 'displayExposedForm']],
125 $form['expose_button']['checkbox']['checkbox']['#default_value'] = 0;
128 $form['expose_button']['markup'] = [
129 '#markup' => '<div class="description exposed-description">' . $this->t('This sort is exposed. If you hide it, users will not be able to change it.') . '</div>',
131 $form['expose_button']['button'] = [
132 '#limit_validation_errors' => [],
134 '#value' => $this->t('Hide sort'),
135 '#submit' => [[$this, 'displayExposedForm']],
137 $form['expose_button']['checkbox']['checkbox']['#default_value'] = 1;
142 * Simple validate handler
144 public function validateOptionsForm(&$form, FormStateInterface $form_state) {
145 $this->sortValidate($form, $form_state);
146 if (!empty($this->options['exposed'])) {
147 $this->validateExposeForm($form, $form_state);
153 * Simple submit handler
155 public function submitOptionsForm(&$form, FormStateInterface $form_state) {
156 // Do not store this values.
157 $form_state->unsetValue('expose_button');
159 $this->sortSubmit($form, $form_state);
160 if (!empty($this->options['exposed'])) {
161 $this->submitExposeForm($form, $form_state);
166 * Shortcut to display the value form.
168 protected function showSortForm(&$form, FormStateInterface $form_state) {
169 $options = $this->sortOptions();
170 if (!empty($options)) {
172 '#title' => $this->t('Order'),
174 '#options' => $options,
175 '#default_value' => $this->options['order'],
180 protected function sortValidate(&$form, FormStateInterface $form_state) {}
182 public function sortSubmit(&$form, FormStateInterface $form_state) {}
185 * Provide a list of options for the default sort form.
186 * Should be overridden by classes that don't override sort_form
188 protected function sortOptions() {
190 'ASC' => $this->t('Sort ascending'),
191 'DESC' => $this->t('Sort descending'),
195 public function buildExposeForm(&$form, FormStateInterface $form_state) {
196 // #flatten will move everything from $form['expose'][$key] to $form[$key]
197 // prior to rendering. That's why the preRender for it needs to run first,
198 // so that when the next preRender (the one for fieldsets) runs, it gets
199 // the flattened data.
200 array_unshift($form['#pre_render'], [get_class($this), 'preRenderFlattenData']);
201 $form['expose']['#flatten'] = TRUE;
203 $form['expose']['label'] = [
204 '#type' => 'textfield',
205 '#default_value' => $this->options['expose']['label'],
206 '#title' => $this->t('Label'),
214 * Provide default options for exposed sorts.
216 public function defaultExposeOptions() {
217 $this->options['expose'] = [
218 'label' => $this->definition['title'],
225 public function getCacheMaxAge() {
226 // The result of a sort does not depend on outside information, so by
227 // default it is cacheable.
228 return Cache::PERMANENT;
234 public function getCacheContexts() {
235 $cache_contexts = [];
236 // Exposed sorts use GET parameters, so it depends on the current URL.
237 if ($this->isExposed()) {
238 $cache_contexts[] = 'url.query_args:sort_by';
240 return $cache_contexts;
246 public function getCacheTags() {