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() { return TRUE; }
34 * Called to add the sort to a query.
36 public function query() {
37 $this->ensureMyTable();
39 $this->query->addOrderBy($this->tableAlias, $this->realField, $this->options['order']);
42 protected function defineOptions() {
43 $options = parent::defineOptions();
45 $options['order'] = ['default' => 'ASC'];
46 $options['exposed'] = ['default' => FALSE];
47 $options['expose'] = [
49 'label' => ['default' => ''],
56 * Display whether or not the sort order is ascending or descending
58 public function adminSummary() {
59 if (!empty($this->options['exposed'])) {
60 return $this->t('Exposed');
62 switch ($this->options['order']) {
66 return $this->t('asc');
70 return $this->t('desc');
75 * Basic options for all sort criteria
77 public function buildOptionsForm(&$form, FormStateInterface $form_state) {
78 parent::buildOptionsForm($form, $form_state);
79 if ($this->canExpose()) {
80 $this->showExposeButton($form, $form_state);
82 $form['op_val_start'] = ['#value' => '<div class="clearfix">'];
83 $this->showSortForm($form, $form_state);
84 $form['op_val_end'] = ['#value' => '</div>'];
85 if ($this->canExpose()) {
86 $this->showExposeForm($form, $form_state);
91 * Shortcut to display the expose/hide button.
93 public function showExposeButton(&$form, FormStateInterface $form_state) {
94 $form['expose_button'] = [
95 '#prefix' => '<div class="views-expose clearfix">',
96 '#suffix' => '</div>',
97 // Should always come first
101 // Add a checkbox for JS users, which will have behavior attached to it
102 // so it can replace the button.
103 $form['expose_button']['checkbox'] = [
104 '#theme_wrappers' => ['container'],
105 '#attributes' => ['class' => ['js-only']],
107 $form['expose_button']['checkbox']['checkbox'] = [
108 '#title' => $this->t('Expose this sort to visitors, to allow them to change it'),
109 '#type' => 'checkbox',
112 // Then add the button itself.
113 if (empty($this->options['exposed'])) {
114 $form['expose_button']['markup'] = [
115 '#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>',
117 $form['expose_button']['button'] = [
118 '#limit_validation_errors' => [],
120 '#value' => $this->t('Expose sort'),
121 '#submit' => [[$this, 'displayExposedForm']],
123 $form['expose_button']['checkbox']['checkbox']['#default_value'] = 0;
126 $form['expose_button']['markup'] = [
127 '#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>',
129 $form['expose_button']['button'] = [
130 '#limit_validation_errors' => [],
132 '#value' => $this->t('Hide sort'),
133 '#submit' => [[$this, 'displayExposedForm']],
135 $form['expose_button']['checkbox']['checkbox']['#default_value'] = 1;
140 * Simple validate handler
142 public function validateOptionsForm(&$form, FormStateInterface $form_state) {
143 $this->sortValidate($form, $form_state);
144 if (!empty($this->options['exposed'])) {
145 $this->validateExposeForm($form, $form_state);
151 * Simple submit handler
153 public function submitOptionsForm(&$form, FormStateInterface $form_state) {
154 // Do not store this values.
155 $form_state->unsetValue('expose_button');
157 $this->sortSubmit($form, $form_state);
158 if (!empty($this->options['exposed'])) {
159 $this->submitExposeForm($form, $form_state);
164 * Shortcut to display the value form.
166 protected function showSortForm(&$form, FormStateInterface $form_state) {
167 $options = $this->sortOptions();
168 if (!empty($options)) {
170 '#title' => $this->t('Order'),
172 '#options' => $options,
173 '#default_value' => $this->options['order'],
178 protected function sortValidate(&$form, FormStateInterface $form_state) { }
180 public function sortSubmit(&$form, FormStateInterface $form_state) { }
183 * Provide a list of options for the default sort form.
184 * Should be overridden by classes that don't override sort_form
186 protected function sortOptions() {
188 'ASC' => $this->t('Sort ascending'),
189 'DESC' => $this->t('Sort descending'),
193 public function buildExposeForm(&$form, FormStateInterface $form_state) {
194 // #flatten will move everything from $form['expose'][$key] to $form[$key]
195 // prior to rendering. That's why the preRender for it needs to run first,
196 // so that when the next preRender (the one for fieldsets) runs, it gets
197 // the flattened data.
198 array_unshift($form['#pre_render'], [get_class($this), 'preRenderFlattenData']);
199 $form['expose']['#flatten'] = TRUE;
201 $form['expose']['label'] = [
202 '#type' => 'textfield',
203 '#default_value' => $this->options['expose']['label'],
204 '#title' => $this->t('Label'),
212 * Provide default options for exposed sorts.
214 public function defaultExposeOptions() {
215 $this->options['expose'] = [
216 'label' => $this->definition['title'],
223 public function getCacheMaxAge() {
224 // The result of a sort does not depend on outside information, so by
225 // default it is cacheable.
226 return Cache::PERMANENT;
232 public function getCacheContexts() {
233 $cache_contexts = [];
234 // Exposed sorts use GET parameters, so it depends on the current URL.
235 if ($this->isExposed()) {
236 $cache_contexts[] = 'url.query_args:sort_by';
238 return $cache_contexts;
244 public function getCacheTags() {