Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  declare(strict_types=1);
  18  
  19  namespace core\output;
  20  
  21  use renderer_base;
  22  
  23  /**
  24   * A single-select combobox widget that is functionally similar to an HTML select element.
  25   *
  26   * @package   core
  27   * @category  output
  28   * @copyright 2022 Shamim Rezaie <shamim@moodle.com>
  29   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class select_menu implements \renderable, \templatable {
  32      /** @var array List of options. */
  33      protected $options;
  34  
  35      /** @var string|null The value of the preselected option. */
  36      protected $selected;
  37  
  38      /** @var string The combobox label */
  39      protected $label;
  40  
  41      /** @var array Button label's attributes */
  42      protected $labelattributes = [];
  43  
  44      /** @var string Name of the combobox element */
  45      protected $name;
  46  
  47      /**
  48       * select_menu constructor.
  49       *
  50       * @param string $name Name of the combobox element
  51       * @param array $options List of options in an associative array format like ['val' => 'Option'].
  52       *                       Supports grouped options as well.
  53       * @param string|null $selected The value of the preselected option.
  54       */
  55      public function __construct(string $name, array $options, string $selected = null) {
  56          $this->name = $name;
  57          $this->options = $options;
  58          $this->selected = $selected;
  59      }
  60  
  61      /**
  62       * Sets the select menu's label.
  63       *
  64       * @param string $label The label.
  65       * @param array $attributes List of attributes to apply on the label element.
  66       */
  67      public function set_label(string $label, array $attributes = []) {
  68          $this->label = $label;
  69          $this->labelattributes = $attributes;
  70      }
  71  
  72      /**
  73       * Flatten the options for Mustache.
  74       *
  75       * @return array
  76       */
  77      protected function flatten_options(): array {
  78          $flattened = [];
  79  
  80          foreach ($this->options as $value => $option) {
  81              if (is_array($option)) {
  82                  foreach ($option as $groupname => $optoptions) {
  83                      if (!isset($flattened[$groupname])) {
  84                          $flattened[$groupname] = [
  85                              'name' => $groupname,
  86                              'isgroup' => true,
  87                              'id' => \html_writer::random_id('select-menu-group'),
  88                              'options' => []
  89                          ];
  90                      }
  91                      foreach ($optoptions as $optvalue => $optoption) {
  92                          $flattened[$groupname]['options'][$optvalue] = [
  93                              'name' => $optoption,
  94                              'value' => $optvalue,
  95                              'selected' => $this->selected == $optvalue,
  96                              'id' => \html_writer::random_id('select-menu-option'),
  97                          ];
  98                      }
  99                  }
 100              } else {
 101                  $flattened[$value] = [
 102                      'name' => $option,
 103                      'value' => $value,
 104                      'selected' => $this->selected == $value,
 105                      'id' => \html_writer::random_id('select-menu-option'),
 106                  ];
 107              }
 108          }
 109  
 110          // Make non-associative array.
 111          foreach ($flattened as $key => $value) {
 112              if (!empty($value['options'])) {
 113                  $flattened[$key]['options'] = array_values($value['options']);
 114              }
 115          }
 116          $flattened = array_values($flattened);
 117  
 118          return $flattened;
 119      }
 120  
 121      /**
 122       * Return the name of the selected option.
 123       *
 124       * @return string|null The name of the selected option or null.
 125       */
 126      private function get_selected_option(): ?string {
 127          foreach ($this->options as $value => $option) {
 128              if (is_array($option)) {  // This is a group.
 129                  foreach ($option as $groupname => $optoptions) {
 130                      // Loop through the options within the group to check whether any of them matches the 'selected' value.
 131                      foreach ($optoptions as $optvalue => $optoption) {
 132                          // If the value of the option matches the 'selected' value, return the name of the option.
 133                          if ($this->selected == $optvalue) {
 134                              return $optoption;
 135                          }
 136                      }
 137                  }
 138              } else { // This is a standard option item.
 139                  // If the value of the option matches the 'selected' value, return the name of the option.
 140                  if ($this->selected == $value) {
 141                      return $option;
 142                  }
 143              }
 144          }
 145          return null;
 146      }
 147  
 148      /**
 149       * Export for template.
 150       *
 151       * @param renderer_base $output The renderer.
 152       * @return \stdClass
 153       */
 154      public function export_for_template(renderer_base $output): \stdClass {
 155          $data = new \stdClass();
 156          $data->baseid = \html_writer::random_id('select-menu');
 157          $data->label = $this->label;
 158          $data->options = $this->flatten_options();
 159          $data->selectedoption = $this->get_selected_option();
 160          $data->name = $this->name;
 161          $data->value = $this->selected;
 162  
 163          // Label attributes.
 164          $data->labelattributes = [];
 165          // Map the label attributes.
 166          foreach ($this->labelattributes as $key => $value) {
 167              $data->labelattributes[] = ['name' => $key, 'value' => $value];
 168          }
 169  
 170          return $data;
 171      }
 172  }