Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

   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  /**
  18   * Class for loading/storing competencies from the DB.
  19   *
  20   * @package    core_competency
  21   * @copyright  2015 Damyon Wiese
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace core_competency;
  25  
  26  use coding_exception;
  27  use lang_string;
  28  use core_course\external\course_summary_exporter;
  29  
  30  /**
  31   * Class for loading/storing course_competencies from the DB.
  32   *
  33   * @copyright  2015 Damyon Wiese
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class course_competency extends persistent {
  37  
  38      const TABLE = 'competency_coursecomp';
  39  
  40      /** Course competency ruleoutcome constant. */
  41      const OUTCOME_NONE = 0;
  42      /** Course competency ruleoutcome constant. */
  43      const OUTCOME_EVIDENCE = 1;
  44      /** Course competency ruleoutcome constant. */
  45      const OUTCOME_RECOMMEND = 2;
  46      /** Course competency ruleoutcome constant. */
  47      const OUTCOME_COMPLETE = 3;
  48  
  49      /**
  50       * Return the definition of the properties of this model.
  51       *
  52       * @return array
  53       */
  54      protected static function define_properties() {
  55          return array(
  56              'courseid' => array(
  57                  'type' => PARAM_INT
  58              ),
  59              'competencyid' => array(
  60                  'type' => PARAM_INT
  61              ),
  62              'sortorder' => array(
  63                  'type' => PARAM_INT
  64              ),
  65              'ruleoutcome' => array(
  66                  'choices' => array(self::OUTCOME_NONE,
  67                      self::OUTCOME_EVIDENCE,
  68                      self::OUTCOME_RECOMMEND,
  69                      self::OUTCOME_COMPLETE
  70                  ),
  71                  'default' => self::OUTCOME_EVIDENCE,
  72                  'type' => PARAM_INT,
  73              ),
  74          );
  75      }
  76  
  77      /**
  78       * Hook to execute before validate.
  79       *
  80       * @return void
  81       */
  82      protected function before_validate() {
  83          if (($this->get('id') && $this->get('sortorder') === null) || !$this->get('id')) {
  84              $this->set('sortorder', $this->count_records(array('courseid' => $this->get('courseid'))));
  85          }
  86      }
  87  
  88      /**
  89       * Return the courses where both competency and user are.
  90       *
  91       * A user is considered being in a course when they are enrolled, the enrolment is valid,
  92       * the enrolment instance is enabled, and the enrolment plugin is enabled..
  93       *
  94       * @param int $competencyid The competency ID.
  95       * @param int $userid The user ID.
  96       * @return array Indexed by course ID.
  97       */
  98      public static function get_courses_with_competency_and_user($competencyid, $userid) {
  99          global $CFG, $DB;
 100  
 101          if (!$plugins = explode(',', $CFG->enrol_plugins_enabled)) {
 102              return array();
 103          }
 104  
 105          $ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
 106          list($plugins, $params) = $DB->get_in_or_equal($plugins, SQL_PARAMS_NAMED, 'ee');
 107          $params['competencyid'] = $competencyid;
 108          $params['userid'] = $userid;
 109          $params['enabled'] = ENROL_INSTANCE_ENABLED;
 110          $params['active'] = ENROL_USER_ACTIVE;
 111          $params['contextlevel'] = CONTEXT_COURSE;
 112  
 113          // Heavily based on enrol_get_shared_courses().
 114          $sql = "SELECT c.*, $ctxfields
 115                    FROM {course} c
 116                    JOIN {" . static::TABLE . "} cc
 117                      ON cc.courseid = c.id
 118                     AND cc.competencyid = :competencyid
 119                    JOIN (
 120                      SELECT DISTINCT c.id
 121                        FROM {enrol} e
 122                        JOIN {user_enrolments} ue
 123                          ON ue.enrolid = e.id
 124                         AND ue.status = :active
 125                         AND ue.userid = :userid
 126                        JOIN {course} c
 127                          ON c.id = e.courseid
 128                       WHERE e.status = :enabled
 129                         AND e.enrol $plugins
 130                    ) ec ON ec.id = c.id
 131               LEFT JOIN {context} ctx
 132                      ON ctx.instanceid = c.id
 133                     AND ctx.contextlevel = :contextlevel
 134                ORDER BY c.id";
 135  
 136          $courses = $DB->get_records_sql($sql, $params);
 137          array_map('context_helper::preload_from_record', $courses);
 138          return $courses;
 139      }
 140  
 141      /**
 142       * Return a list of rules.
 143       *
 144       * @return array Indexed by outcome value.
 145       */
 146      public static function get_ruleoutcome_list() {
 147          static $list = null;
 148  
 149          if ($list === null) {
 150              $list = array(
 151                  self::OUTCOME_NONE => self::get_ruleoutcome_name(self::OUTCOME_NONE),
 152                  self::OUTCOME_EVIDENCE => self::get_ruleoutcome_name(self::OUTCOME_EVIDENCE),
 153                  self::OUTCOME_RECOMMEND => self::get_ruleoutcome_name(self::OUTCOME_RECOMMEND),
 154                  self::OUTCOME_COMPLETE => self::get_ruleoutcome_name(self::OUTCOME_COMPLETE));
 155          }
 156  
 157          return $list;
 158      }
 159  
 160      /**
 161       * Human readable rule name.
 162       *
 163       * @param int $ruleoutcome The value of ruleoutcome.
 164       * @return lang_string
 165       */
 166      public static function get_ruleoutcome_name($ruleoutcome) {
 167  
 168          switch ($ruleoutcome) {
 169              case self::OUTCOME_NONE:
 170                  $strname = 'none';
 171                  break;
 172              case self::OUTCOME_EVIDENCE:
 173                  $strname = 'evidence';
 174                  break;
 175              case self::OUTCOME_RECOMMEND:
 176                  $strname = 'recommend';
 177                  break;
 178              case self::OUTCOME_COMPLETE:
 179                  $strname = 'complete';
 180                  break;
 181              default:
 182                  throw new \moodle_exception('errorcoursecompetencyrule', 'core_competency', '', $ruleoutcome);
 183                  break;
 184          }
 185  
 186          return new lang_string('coursecompetencyoutcome_' . $strname, 'core_competency');
 187      }
 188  
 189      /**
 190       * Validate course ID.
 191       *
 192       * @param int $data The course ID.
 193       * @return true|lang_string
 194       */
 195      protected function validate_courseid($data) {
 196          global $DB;
 197          if (!$DB->record_exists('course', array('id' => $data))) {
 198              return new lang_string('invalidcourseid', 'error');
 199          }
 200          return true;
 201      }
 202  
 203      /**
 204       * Validate competency ID.
 205       *
 206       * @param int $data The competency ID.
 207       * @return true|lang_string
 208       */
 209      protected function validate_competencyid($data) {
 210          if (!competency::record_exists($data)) {
 211              return new lang_string('invaliddata', 'error');
 212          }
 213          return true;
 214      }
 215  
 216      /**
 217       * Return the course IDs and visible flags that include this competency.
 218       *
 219       * Only the ids and visible flag are returned, for the full records use list_courses.
 220       *
 221       * @param int $competencyid The competency id
 222       * @return array containing courseid and visible.
 223       */
 224      public static function list_courses_min($competencyid) {
 225          global $DB;
 226  
 227          $results = $DB->get_records_sql('SELECT course.id as id, course.visible as visible
 228                                             FROM {' . self::TABLE . '} coursecomp
 229                                             JOIN {course} course
 230                                               ON coursecomp.courseid = course.id
 231                                            WHERE coursecomp.competencyid = ? ', array($competencyid));
 232  
 233          return $results;
 234      }
 235  
 236      /**
 237       * Return partial course records foreach course that contains this competency.
 238       *
 239       * @param int $competencyid The competency id
 240       * @return array[stdClass] Array of course records containg id, visible, shortname, idnumber, fullname
 241       */
 242      public static function list_courses($competencyid) {
 243          global $DB;
 244  
 245          // We need all the course summary exporter properties, plus category.
 246          $coursefields = course_summary_exporter::properties_definition();
 247          $coursefields = array_map(function(string $field): string {
 248              return "course.{$field}";
 249          }, array_keys($coursefields));
 250  
 251          $results = $DB->get_records_sql('SELECT ' . implode(',', $coursefields) . ', course.category
 252                                             FROM {course} course
 253                                             JOIN {' . self::TABLE . '} coursecomp
 254                                               ON coursecomp.courseid = course.id
 255                                            WHERE coursecomp.competencyid = ? ', array($competencyid));
 256  
 257          return $results;
 258      }
 259  
 260      /**
 261       * Count the competencies in this course.
 262       *
 263       * @param int $courseid The course id
 264       * @return int
 265       */
 266      public static function count_competencies($courseid) {
 267          global $DB;
 268  
 269          $sql = 'SELECT COUNT(comp.id)
 270                    FROM {' . self::TABLE . '} coursecomp
 271                    JOIN {' . competency::TABLE . '} comp
 272                      ON coursecomp.competencyid = comp.id
 273                   WHERE coursecomp.courseid = ? ';
 274          $params = array($courseid);
 275  
 276          $results = $DB->count_records_sql($sql, $params);
 277  
 278          return $results;
 279      }
 280  
 281      /**
 282       * List the competencies in this course.
 283       *
 284       * @param int $courseid The course id
 285       * @return competency[] Indexed by competency ID.
 286       */
 287      public static function list_competencies($courseid) {
 288          global $DB;
 289  
 290          $sql = 'SELECT comp.*
 291                    FROM {' . competency::TABLE . '} comp
 292                    JOIN {' . self::TABLE . '} coursecomp
 293                      ON coursecomp.competencyid = comp.id
 294                   WHERE coursecomp.courseid = ?';
 295          $params = array($courseid);
 296  
 297          $sql .= ' ORDER BY coursecomp.sortorder ASC';
 298          $results = $DB->get_recordset_sql($sql, $params);
 299          $instances = array();
 300          foreach ($results as $result) {
 301              $comp = new competency(0, $result);
 302              $instances[$comp->get('id')] = $comp;
 303          }
 304          $results->close();
 305  
 306          return $instances;
 307      }
 308  
 309      /**
 310       * Get a single competency from the course (only if it is really in the course).
 311       *
 312       * @param int $courseid The course id
 313       * @param int $competencyid The competency id
 314       * @return competency
 315       */
 316      public static function get_competency($courseid, $competencyid) {
 317          global $DB;
 318  
 319          $sql = 'SELECT comp.*
 320                    FROM {' . competency::TABLE . '} comp
 321                    JOIN {' . self::TABLE . '} crscomp
 322                      ON crscomp.competencyid = comp.id
 323                   WHERE crscomp.courseid = ? AND crscomp.competencyid = ?';
 324          $params = array($courseid, $competencyid);
 325  
 326          $result = $DB->get_record_sql($sql, $params);
 327          if (!$result) {
 328              throw new coding_exception('The competency does not belong to this course: ' . $competencyid . ', ' . $courseid);
 329          }
 330  
 331          return new competency(0, $result);
 332      }
 333  
 334      /**
 335       * Hook to execute after delete.
 336       *
 337       * @param bool $result Whether or not the delete was successful.
 338       * @return void
 339       */
 340      protected function after_delete($result) {
 341          global $DB;
 342          if (!$result) {
 343              return;
 344          }
 345  
 346          $table = '{' . self::TABLE . '}';
 347          $sql = "UPDATE $table SET sortorder = sortorder -1  WHERE courseid = ? AND sortorder > ?";
 348          $DB->execute($sql, array($this->get('courseid'), $this->get('sortorder')));
 349      }
 350  
 351      /**
 352       * Get the specified course_competency in this course.
 353       *
 354       * @param int $courseid The course id
 355       * @param int $competencyid The competency id
 356       * @return course_competency
 357       */
 358      public static function get_course_competency($courseid, $competencyid) {
 359          global $DB;
 360  
 361          $sql = 'SELECT crscomp.*
 362                    FROM {' . self::TABLE . '} crscomp
 363                   WHERE crscomp.courseid = ? AND crscomp.competencyid = ?';
 364          $params = array($courseid, $competencyid);
 365  
 366          $result = $DB->get_record_sql($sql, $params);
 367          if (!$result) {
 368              throw new coding_exception('The competency does not belong to this course: ' . $competencyid . ', ' . $courseid);
 369          }
 370  
 371          return new course_competency(0, $result);
 372      }
 373  
 374      /**
 375       * List the course_competencies in this course.
 376       *
 377       * @param int $courseid The course id
 378       * @return course_competency[]
 379       */
 380      public static function list_course_competencies($courseid) {
 381          global $DB;
 382  
 383          $sql = 'SELECT coursecomp.*
 384                    FROM {' . self::TABLE . '} coursecomp
 385                    JOIN {' . competency::TABLE . '} comp
 386                      ON coursecomp.competencyid = comp.id
 387                   WHERE coursecomp.courseid = ?';
 388          $params = array($courseid);
 389  
 390          $sql .= ' ORDER BY coursecomp.sortorder ASC';
 391          $results = $DB->get_recordset_sql($sql, $params);
 392          $instances = array();
 393          foreach ($results as $result) {
 394              array_push($instances, new course_competency(0, $result));
 395          }
 396          $results->close();
 397  
 398          return $instances;
 399      }
 400  
 401      /**
 402       * Check if course competency has records for competencies.
 403       *
 404       * @param array $competencyids Array of competencies ids.
 405       * @return boolean Return true if one or more than a competency was found in a course.
 406       */
 407      public static function has_records_for_competencies($competencyids) {
 408          global $DB;
 409          list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED);
 410          return self::record_exists_select("competencyid $insql", $params);
 411      }
 412  
 413  }