Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

   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   * Contains the favourite_repository class, responsible for CRUD operations for favourites.
  18   *
  19   * @package   core_favourites
  20   * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
  21   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  namespace core_favourites\local\repository;
  24  use \core_favourites\local\entity\favourite;
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Class favourite_repository.
  30   *
  31   * This class handles persistence of favourites. Favourites from all areas are supported by this repository.
  32   *
  33   * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
  34   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class favourite_repository implements favourite_repository_interface {
  37  
  38      /**
  39       * @var string the name of the table which favourites are stored in.
  40       */
  41      protected $favouritetable = 'favourite';
  42  
  43      /**
  44       * Get a favourite object, based on a full record.
  45       * @param \stdClass $record the record we wish to hydrate.
  46       * @return favourite the favourite record.
  47       */
  48      protected function get_favourite_from_record(\stdClass $record) : favourite {
  49          $favourite = new favourite(
  50              $record->component,
  51              $record->itemtype,
  52              $record->itemid,
  53              $record->contextid,
  54              $record->userid
  55          );
  56          $favourite->id = $record->id;
  57          $favourite->ordering = $record->ordering ?? null;
  58          $favourite->timecreated = $record->timecreated ?? null;
  59          $favourite->timemodified = $record->timemodified ?? null;
  60  
  61          return $favourite;
  62      }
  63  
  64      /**
  65       * Get a list of favourite objects, based on a list of records.
  66       * @param array $records the record we wish to hydrate.
  67       * @return array the list of favourites.
  68       */
  69      protected function get_list_of_favourites_from_records(array $records) {
  70          $list = [];
  71          foreach ($records as $index => $record) {
  72              $list[$index] = $this->get_favourite_from_record($record);
  73          }
  74          return $list;
  75      }
  76  
  77      /**
  78       * Basic validation, confirming we have the minimum field set needed to save a record to the store.
  79       *
  80       * @param favourite $favourite the favourite record to validate.
  81       * @throws \moodle_exception if the supplied favourite has missing or unsupported fields.
  82       */
  83      protected function validate(favourite $favourite) {
  84  
  85          $favourite = (array)$favourite;
  86  
  87          // The allowed fields, and whether or not each is required to create a record.
  88          // The timecreated, timemodified and id fields are generated during create/update.
  89          $allowedfields = [
  90              'userid' => true,
  91              'component' => true,
  92              'itemtype' => true,
  93              'itemid' => true,
  94              'contextid' => true,
  95              'ordering' => false,
  96              'timecreated' => false,
  97              'timemodified' => false,
  98              'id' => false,
  99              'uniquekey' => false
 100          ];
 101  
 102          $requiredfields = array_filter($allowedfields, function($field) {
 103              return $field;
 104          });
 105  
 106          if ($missingfields = array_keys(array_diff_key($requiredfields, $favourite))) {
 107              throw new \moodle_exception("Missing object property(s) '" . join(', ', $missingfields) . "'.");
 108          }
 109  
 110          // If the record contains fields we don't allow, throw an exception.
 111          if ($unsupportedfields = array_keys(array_diff_key($favourite, $allowedfields))) {
 112              throw new \moodle_exception("Unexpected object property(s) '" . join(', ', $unsupportedfields) . "'.");
 113          }
 114      }
 115  
 116      /**
 117       * Add a favourite to the repository.
 118       *
 119       * @param favourite $favourite the favourite to add.
 120       * @return favourite the favourite which has been stored.
 121       * @throws \dml_exception if any database errors are encountered.
 122       * @throws \moodle_exception if the favourite has missing or invalid properties.
 123       */
 124      public function add(favourite $favourite) : favourite {
 125          global $DB;
 126          $this->validate($favourite);
 127          $favourite = (array)$favourite;
 128          $time = time();
 129          $favourite['timecreated'] = $time;
 130          $favourite['timemodified'] = $time;
 131          $id = $DB->insert_record($this->favouritetable, $favourite);
 132          return $this->find($id);
 133      }
 134  
 135      /**
 136       * Add a collection of favourites to the repository.
 137       *
 138       * @param array $items the list of favourites to add.
 139       * @return array the list of favourites which have been stored.
 140       * @throws \dml_exception if any database errors are encountered.
 141       * @throws \moodle_exception if any of the favourites have missing or invalid properties.
 142       */
 143      public function add_all(array $items) : array {
 144          global $DB;
 145          $time = time();
 146          foreach ($items as $item) {
 147              $this->validate($item);
 148              $favourite = (array)$item;
 149              $favourite['timecreated'] = $time;
 150              $favourite['timemodified'] = $time;
 151              $ids[] = $DB->insert_record($this->favouritetable, $favourite);
 152          }
 153          list($insql, $params) = $DB->get_in_or_equal($ids);
 154          $records = $DB->get_records_select($this->favouritetable, "id $insql", $params);
 155          return $this->get_list_of_favourites_from_records($records);
 156      }
 157  
 158      /**
 159       * Find a favourite by id.
 160       *
 161       * @param int $id the id of the favourite.
 162       * @return favourite the favourite.
 163       * @throws \dml_exception if any database errors are encountered.
 164       */
 165      public function find(int $id) : favourite {
 166          global $DB;
 167          $record = $DB->get_record($this->favouritetable, ['id' => $id], '*', MUST_EXIST);
 168          return $this->get_favourite_from_record($record);
 169      }
 170  
 171      /**
 172       * Return all items in this repository, as an array, indexed by id.
 173       *
 174       * @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
 175       * @param int $limitnum optional pagination control for returning a subset comprising this many records.
 176       * @return array the list of all favourites stored within this repository.
 177       * @throws \dml_exception if any database errors are encountered.
 178       */
 179      public function find_all(int $limitfrom = 0, int $limitnum = 0) : array {
 180          global $DB;
 181          $records = $DB->get_records($this->favouritetable, null, '', '*', $limitfrom, $limitnum);
 182          return $this->get_list_of_favourites_from_records($records);
 183      }
 184  
 185      /**
 186       * Return all items matching the supplied criteria (a [key => value,..] list).
 187       *
 188       * @param array $criteria the list of key/value(s) criteria pairs.
 189       * @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
 190       * @param int $limitnum optional pagination control for returning a subset comprising this many records.
 191       * @return array the list of favourites matching the criteria.
 192       * @throws \dml_exception if any database errors are encountered.
 193       */
 194      public function find_by(array $criteria, int $limitfrom = 0, int $limitnum = 0) : array {
 195          global $DB;
 196          $conditions = [];
 197          $params = [];
 198          foreach ($criteria as $field => $value) {
 199              if (is_array($value) && count($value)) {
 200                  list($insql, $inparams) = $DB->get_in_or_equal($value, SQL_PARAMS_NAMED);
 201                  $conditions[] = "$field $insql";
 202                  $params = array_merge($params, $inparams);
 203              } else {
 204                  $conditions[] = "$field = :$field";
 205                  $params = array_merge($params, [$field => $value]);
 206              }
 207          }
 208  
 209          $records = $DB->get_records_select($this->favouritetable, implode(' AND ', $conditions), $params,
 210              '', '*', $limitfrom, $limitnum);
 211  
 212          return $this->get_list_of_favourites_from_records($records);
 213      }
 214  
 215      /**
 216       * Find a specific favourite, based on the properties known to identify it.
 217       *
 218       * Used if we don't know its id.
 219       *
 220       * @param int $userid the id of the user to which the favourite belongs.
 221       * @param string $component the frankenstyle component name.
 222       * @param string $itemtype the type of the favourited item.
 223       * @param int $itemid the id of the item which was favourited (not the favourite's id).
 224       * @param int $contextid the contextid of the item which was favourited.
 225       * @return favourite the favourite.
 226       * @throws \dml_exception if any database errors are encountered or if the record could not be found.
 227       */
 228      public function find_favourite(int $userid, string $component, string $itemtype, int $itemid, int $contextid) : favourite {
 229          global $DB;
 230          // Favourites model: We know that only one favourite can exist based on these properties.
 231          $record = $DB->get_record($this->favouritetable, [
 232              'userid' => $userid,
 233              'component' => $component,
 234              'itemtype' => $itemtype,
 235              'itemid' => $itemid,
 236              'contextid' => $contextid
 237          ], '*', MUST_EXIST);
 238          return $this->get_favourite_from_record($record);
 239      }
 240  
 241      /**
 242       * Check whether a favourite exists in this repository, based on its id.
 243       *
 244       * @param int $id the id to search for.
 245       * @return bool true if the favourite exists, false otherwise.
 246       * @throws \dml_exception if any database errors are encountered.
 247       */
 248      public function exists(int $id) : bool {
 249          global $DB;
 250          return $DB->record_exists($this->favouritetable, ['id' => $id]);
 251      }
 252  
 253      /**
 254       * Check whether an item exists in this repository, based on the specified criteria.
 255       *
 256       * @param array $criteria the list of key/value criteria pairs.
 257       * @return bool true if the favourite exists, false otherwise.
 258       * @throws \dml_exception if any database errors are encountered.
 259       */
 260      public function exists_by(array $criteria) : bool {
 261          global $DB;
 262          return $DB->record_exists($this->favouritetable, $criteria);
 263      }
 264  
 265      /**
 266       * Update a favourite.
 267       *
 268       * @param favourite $favourite the favourite to update.
 269       * @return favourite the updated favourite.
 270       * @throws \dml_exception if any database errors are encountered.
 271       */
 272      public function update(favourite $favourite) : favourite {
 273          global $DB;
 274          $time = time();
 275          $favourite->timemodified = $time;
 276          $DB->update_record($this->favouritetable, $favourite);
 277          return $this->find($favourite->id);
 278      }
 279  
 280      /**
 281       * Delete a favourite, by id.
 282       *
 283       * @param int $id the id of the favourite to delete.
 284       * @throws \dml_exception if any database errors are encountered.
 285       */
 286      public function delete(int $id) {
 287          global $DB;
 288          $DB->delete_records($this->favouritetable, ['id' => $id]);
 289      }
 290  
 291      /**
 292       * Delete all favourites matching the specified criteria.
 293       *
 294       * @param array $criteria the list of key/value criteria pairs.
 295       * @throws \dml_exception if any database errors are encountered.
 296       */
 297      public function delete_by(array $criteria) {
 298          global $DB;
 299          $DB->delete_records($this->favouritetable, $criteria);
 300      }
 301  
 302      /**
 303       * Return the total number of favourites in this repository.
 304       *
 305       * @return int the total number of items.
 306       * @throws \dml_exception if any database errors are encountered.
 307       */
 308      public function count() : int {
 309          global $DB;
 310          return $DB->count_records($this->favouritetable);
 311      }
 312  
 313      /**
 314       * Return the number of user favourites matching the specified criteria.
 315       *
 316       * @param array $criteria the list of key/value criteria pairs.
 317       * @return int the number of favourites matching the criteria.
 318       * @throws \dml_exception if any database errors are encountered.
 319       */
 320      public function count_by(array $criteria) : int {
 321          global $DB;
 322          return $DB->count_records($this->favouritetable, $criteria);
 323      }
 324  }