[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/competency/classes/ -> persistent.php (source)

   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   * Abstract class for core_competency objects saved to 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  defined('MOODLE_INTERNAL') || die();
  26  
  27  use coding_exception;
  28  use invalid_parameter_exception;
  29  use lang_string;
  30  use ReflectionMethod;
  31  use stdClass;
  32  use renderer_base;
  33  
  34  /**
  35   * Abstract class for core_competency objects saved to the DB.
  36   *
  37   * @copyright  2015 Damyon Wiese
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  abstract class persistent {
  41  
  42      /** The table name. */
  43      const TABLE = null;
  44  
  45      /** @var array The model data. */
  46      private $data = array();
  47  
  48      /** @var array The list of validation errors. */
  49      private $errors = array();
  50  
  51      /** @var boolean If the data was already validated. */
  52      private $validated = false;
  53  
  54      /**
  55       * Create an instance of this class.
  56       *
  57       * @param int $id If set, this is the id of an existing record, used to load the data.
  58       * @param stdClass $record If set will be passed to {@link self::from_record()}.
  59       */
  60      public function __construct($id = 0, stdClass $record = null) {
  61          if ($id > 0) {
  62              $this->set('id', $id);
  63              $this->read();
  64          }
  65          if (!empty($record)) {
  66              $this->from_record($record);
  67          }
  68      }
  69  
  70      /**
  71       * Magic method to capture getters and setters.
  72       *
  73       * @param  string $method Callee.
  74       * @param  array $arguments List of arguments.
  75       * @return mixed
  76       */
  77      final public function __call($method, $arguments) {
  78          if (strpos($method, 'get_') === 0) {
  79              return $this->get(substr($method, 4));
  80          } else if (strpos($method, 'set_') === 0) {
  81              return $this->set(substr($method, 4), $arguments[0]);
  82          }
  83          throw new coding_exception('Unexpected method call: ' . $method);
  84      }
  85  
  86      /**
  87       * Data getter.
  88       *
  89       * This is the main getter for all the properties. Developers can implement their own getters
  90       * but they should be calling {@link self::get()} in order to retrieve the value. Essentially
  91       * the getters defined by the developers would only ever be used as helper methods and will not
  92       * be called internally at this stage. In other words, do not expect {@link self::to_record()} or
  93       * {@link self::from_record()} to use them.
  94       *
  95       * This is protected because we wouldn't want the developers to get into the habit of
  96       * using $persistent->get('property_name'), the lengthy getters must be used.
  97       *
  98       * @param  string $property The property name.
  99       * @return mixed
 100       */
 101      final protected function get($property) {
 102          if (!static::has_property($property)) {
 103              throw new coding_exception('Unexpected property \'' . s($property) .'\' requested.');
 104          }
 105          if (!array_key_exists($property, $this->data) && !static::is_property_required($property)) {
 106              $this->set($property, static::get_property_default_value($property));
 107          }
 108          return isset($this->data[$property]) ? $this->data[$property] : null;
 109      }
 110  
 111      /**
 112       * Data setter.
 113       *
 114       * This is the main setter for all the properties. Developers can implement their own setters
 115       * but they should always be calling {@link self::set()} in order to set the value. Essentially
 116       * the setters defined by the developers are helper methods and will not be called internally
 117       * at this stage. In other words do not expect {@link self::to_record()} or
 118       * {@link self::from_record()} to use them.
 119       *
 120       * This is protected because we wouldn't want the developers to get into the habit of
 121       * using $persistent->set('property_name', ''), the lengthy setters must be used.
 122       *
 123       * @param  string $property The property name.
 124       * @param  mixed $value The value.
 125       * @return mixed
 126       */
 127      final protected function set($property, $value) {
 128          if (!static::has_property($property)) {
 129              throw new coding_exception('Unexpected property \'' . s($property) .'\' requested.');
 130          }
 131          if (!array_key_exists($property, $this->data) || $this->data[$property] != $value) {
 132              // If the value is changing, we invalidate the model.
 133              $this->validated = false;
 134          }
 135          $this->data[$property] = $value;
 136      }
 137  
 138      /**
 139       * Return the custom definition of the properties of this model.
 140       *
 141       * Each property MUST be listed here.
 142       *
 143       * The result of this method is cached internally for the whole request.
 144       *
 145       * The 'default' value can be a Closure when its value may change during a single request.
 146       * For example if the default value is based on a $CFG property, then it should be wrapped in a closure
 147       * to avoid running into scenarios where the true value of $CFG is not reflected in the definition.
 148       * Do not abuse closures as they obviously add some overhead.
 149       *
 150       * Examples:
 151       *
 152       * array(
 153       *     'property_name' => array(
 154       *         'default' => 'Default value',        // When not set, the property is considered as required.
 155       *         'message' => new lang_string(...),   // Defaults to invalid data error message.
 156       *         'null' => NULL_ALLOWED,              // Defaults to NULL_NOT_ALLOWED. Takes NULL_NOW_ALLOWED or NULL_ALLOWED.
 157       *         'type' => PARAM_TYPE,                // Mandatory.
 158       *         'choices' => array(1, 2, 3)          // An array of accepted values.
 159       *     )
 160       * )
 161       *
 162       * array(
 163       *     'dynamic_property_name' => array(
 164       *         'default' => function() {
 165       *             return $CFG->something;
 166       *         },
 167       *         'type' => PARAM_INT,
 168       *     )
 169       * )
 170       *
 171       * @return array Where keys are the property names.
 172       */
 173      protected static function define_properties() {
 174          return array();
 175      }
 176  
 177      /**
 178       * Get the properties definition of this model..
 179       *
 180       * @return array
 181       */
 182      final public static function properties_definition() {
 183          global $CFG;
 184  
 185          static $def = null;
 186          if ($def !== null) {
 187              return $def;
 188          }
 189  
 190          $def = static::define_properties();
 191          $def['id'] = array(
 192              'default' => 0,
 193              'type' => PARAM_INT,
 194          );
 195          $def['timecreated'] = array(
 196              'default' => 0,
 197              'type' => PARAM_INT,
 198          );
 199          $def['timemodified'] = array(
 200              'default' => 0,
 201              'type' => PARAM_INT
 202          );
 203          $def['usermodified'] = array(
 204              'default' => 0,
 205              'type' => PARAM_INT
 206          );
 207  
 208          // List of reserved property names. Mostly because we have methods (getters/setters) which would confict with them.
 209          // Think about backwards compability before adding new ones here!
 210          $reserved = array('errors', 'formatted_properties', 'records', 'records_select', 'property_default_value',
 211              'property_error_message', 'sql_fields');
 212  
 213          foreach ($def as $property => $definition) {
 214  
 215              // Ensures that the null property is always set.
 216              if (!array_key_exists('null', $definition)) {
 217                  $def[$property]['null'] = NULL_NOT_ALLOWED;
 218              }
 219  
 220              // Warn the developers when they are doing something wrong.
 221              if ($CFG->debugdeveloper) {
 222                  if (!array_key_exists('type', $definition)) {
 223                      throw new coding_exception('Missing type for: ' . $property);
 224  
 225                  } else if (isset($definition['message']) && !($definition['message'] instanceof lang_string)) {
 226                      throw new coding_exception('Invalid error message for: ' . $property);
 227  
 228                  } else if (in_array($property, $reserved)) {
 229                      throw new coding_exception('This property cannot be defined: ' . $property);
 230  
 231                  }
 232              }
 233          }
 234  
 235          return $def;
 236      }
 237  
 238      /**
 239       * Gets all the formatted properties.
 240       *
 241       * Formatted properties are properties which have a format associated with them.
 242       *
 243       * @return array Keys are property names, values are property format names.
 244       */
 245      final public static function get_formatted_properties() {
 246          $properties = static::properties_definition();
 247  
 248          $formatted = array();
 249          foreach ($properties as $property => $definition) {
 250              $propertyformat = $property . 'format';
 251              if ($definition['type'] == PARAM_RAW && array_key_exists($propertyformat, $properties)
 252                      && $properties[$propertyformat]['type'] == PARAM_INT) {
 253                  $formatted[$property] = $propertyformat;
 254              }
 255          }
 256  
 257          return $formatted;
 258      }
 259  
 260      /**
 261       * Gets the default value for a property.
 262       *
 263       * This assumes that the property exists.
 264       *
 265       * @param string $property The property name.
 266       * @return mixed
 267       */
 268      final protected static function get_property_default_value($property) {
 269          $properties = static::properties_definition();
 270          if (!isset($properties[$property]['default'])) {
 271              return null;
 272          }
 273          $value = $properties[$property]['default'];
 274          if ($value instanceof \Closure) {
 275              return $value();
 276          }
 277          return $value;
 278      }
 279  
 280      /**
 281       * Gets the error message for a property.
 282       *
 283       * This assumes that the property exists.
 284       *
 285       * @param string $property The property name.
 286       * @return lang_string
 287       */
 288      final protected static function get_property_error_message($property) {
 289          $properties = static::properties_definition();
 290          if (!isset($properties[$property]['message'])) {
 291              return new lang_string('invaliddata', 'error');
 292          }
 293          return $properties[$property]['message'];
 294      }
 295  
 296      /**
 297       * Returns whether or not a property was defined.
 298       *
 299       * @param  string $property The property name.
 300       * @return boolean
 301       */
 302      final public static function has_property($property) {
 303          $properties = static::properties_definition();
 304          return isset($properties[$property]);
 305      }
 306  
 307      /**
 308       * Returns whether or not a property is required.
 309       *
 310       * By definition a property with a default value is not required.
 311       *
 312       * @param  string $property The property name.
 313       * @return boolean
 314       */
 315      final public static function is_property_required($property) {
 316          $properties = static::properties_definition();
 317          return !array_key_exists('default', $properties[$property]);
 318      }
 319  
 320      /**
 321       * Populate this class with data from a DB record.
 322       *
 323       * Note that this does not use any custom setter because the data here is intended to
 324       * represent what is stored in the database.
 325       *
 326       * @param \stdClass $record A DB record.
 327       * @return persistent
 328       */
 329      final public function from_record(stdClass $record) {
 330          $record = (array) $record;
 331          foreach ($record as $property => $value) {
 332              $this->set($property, $value);
 333          }
 334          return $this;
 335      }
 336  
 337      /**
 338       * Create a DB record from this class.
 339       *
 340       * Note that this does not use any custom getter because the data here is intended to
 341       * represent what is stored in the database.
 342       *
 343       * @return \stdClass
 344       */
 345      final public function to_record() {
 346          $data = new stdClass();
 347          $properties = static::properties_definition();
 348          foreach ($properties as $property => $definition) {
 349              $data->$property = $this->get($property);
 350          }
 351          return $data;
 352      }
 353  
 354      /**
 355       * Load the data from the DB.
 356       *
 357       * @return persistent
 358       */
 359      final public function read() {
 360          global $DB;
 361  
 362          if ($this->get_id() <= 0) {
 363              throw new coding_exception('id is required to load');
 364          }
 365          $record = $DB->get_record(static::TABLE, array('id' => $this->get_id()), '*', MUST_EXIST);
 366          $this->from_record($record);
 367  
 368          // Validate the data as it comes from the database.
 369          $this->validated = true;
 370  
 371          return $this;
 372      }
 373  
 374      /**
 375       * Hook to execute before a create.
 376       *
 377       * Please note that at this stage the data has already been validated and therefore
 378       * any new data being set will not be validated before it is sent to the database.
 379       *
 380       * This is only intended to be used by child classes, do not put any logic here!
 381       *
 382       * @return void
 383       */
 384      protected function before_create() {
 385      }
 386  
 387      /**
 388       * Insert a record in the DB.
 389       *
 390       * @return persistent
 391       */
 392      final public function create() {
 393          global $DB, $USER;
 394  
 395          if ($this->get_id()) {
 396              // The validation methods rely on the ID to know if we're updating or not, the ID should be
 397              // falsy whenever we are creating an object.
 398              throw new coding_exception('Cannot create an object that has an ID defined.');
 399          }
 400  
 401          if (!$this->is_valid()) {
 402              throw new invalid_persistent_exception($this->get_errors());
 403          }
 404  
 405          // Before create hook.
 406          $this->before_create();
 407  
 408          // We can safely set those values bypassing the validation because we know what we're doing.
 409          $now = time();
 410          $this->set('timecreated', $now);
 411          $this->set('timemodified', $now);
 412          $this->set('usermodified', $USER->id);
 413  
 414          $record = $this->to_record();
 415          unset($record->id);
 416  
 417          $id = $DB->insert_record(static::TABLE, $record);
 418          $this->set('id', $id);
 419  
 420          // We ensure that this is flagged as validated.
 421          $this->validated = true;
 422  
 423          // After create hook.
 424          $this->after_create();
 425  
 426          return $this;
 427      }
 428  
 429      /**
 430       * Hook to execute after a create.
 431       *
 432       * This is only intended to be used by child classes, do not put any logic here!
 433       *
 434       * @return void
 435       */
 436      protected function after_create() {
 437      }
 438  
 439      /**
 440       * Hook to execute before an update.
 441       *
 442       * Please note that at this stage the data has already been validated and therefore
 443       * any new data being set will not be validated before it is sent to the database.
 444       *
 445       * This is only intended to be used by child classes, do not put any logic here!
 446       *
 447       * @return void
 448       */
 449      protected function before_update() {
 450      }
 451  
 452      /**
 453       * Update the existing record in the DB.
 454       *
 455       * @return bool True on success.
 456       */
 457      final public function update() {
 458          global $DB, $USER;
 459  
 460          if ($this->get_id() <= 0) {
 461              throw new coding_exception('id is required to update');
 462          } else if (!$this->is_valid()) {
 463              throw new invalid_persistent_exception($this->get_errors());
 464          }
 465  
 466          // Before update hook.
 467          $this->before_update();
 468  
 469          // We can safely set those values after the validation because we know what we're doing.
 470          $this->set('timemodified', time());
 471          $this->set('usermodified', $USER->id);
 472  
 473          $record = $this->to_record();
 474          unset($record->timecreated);
 475          $record = (array) $record;
 476  
 477          // Save the record.
 478          $result = $DB->update_record(static::TABLE, $record);
 479  
 480          // We ensure that this is flagged as validated.
 481          $this->validated = true;
 482  
 483          // After update hook.
 484          $this->after_update($result);
 485  
 486          return $result;
 487      }
 488  
 489      /**
 490       * Hook to execute after an update.
 491       *
 492       * This is only intended to be used by child classes, do not put any logic here!
 493       *
 494       * @param bool $result Whether or not the update was successful.
 495       * @return void
 496       */
 497      protected function after_update($result) {
 498      }
 499  
 500      /**
 501       * Hook to execute before a delete.
 502       *
 503       * This is only intended to be used by child classes, do not put any logic here!
 504       *
 505       * @return void
 506       */
 507      protected function before_delete() {
 508      }
 509  
 510      /**
 511       * Delete an entry from the database.
 512       *
 513       * @return bool True on success.
 514       */
 515      final public function delete() {
 516          global $DB;
 517  
 518          if ($this->get_id() <= 0) {
 519              throw new coding_exception('id is required to delete');
 520          }
 521  
 522          // Hook before delete.
 523          $this->before_delete();
 524  
 525          $result = $DB->delete_records(static::TABLE, array('id' => $this->get_id()));
 526  
 527          // Hook after delete.
 528          $this->after_delete($result);
 529  
 530          // Reset the ID to avoid any confusion, this also invalidates the model's data.
 531          if ($result) {
 532              $this->set('id', 0);
 533          }
 534  
 535          return $result;
 536      }
 537  
 538      /**
 539       * Hook to execute after a delete.
 540       *
 541       * This is only intended to be used by child classes, do not put any logic here!
 542       *
 543       * @param bool $result Whether or not the delete was successful.
 544       * @return void
 545       */
 546      protected function after_delete($result) {
 547      }
 548  
 549      /**
 550       * Hook to execute before the validation.
 551       *
 552       * This hook will not affect the validation results in any way but is useful to
 553       * internally set properties which will need to be validated.
 554       *
 555       * This is only intended to be used by child classes, do not put any logic here!
 556       *
 557       * @return void
 558       */
 559      protected function before_validate() {
 560      }
 561  
 562      /**
 563       * Validates the data.
 564       *
 565       * Developers can implement addition validation by defining a method as follows. Note that
 566       * the method MUST return a lang_string() when there is an error, and true when the data is valid.
 567       *
 568       * protected function validate_propertyname($value) {
 569       *     if ($value !== 'My expected value') {
 570       *         return new lang_string('invaliddata', 'error');
 571       *     }
 572       *     return true
 573       * }
 574       *
 575       * It is OK to use other properties in your custom validation methods when you need to, however note
 576       * they might not have been validated yet, so try not to rely on them too much.
 577       *
 578       * Note that the validation methods should be protected. Validating just one field is not
 579       * recommended because of the possible dependencies between one field and another,also the
 580       * field ID can be used to check whether the object is being updated or created.
 581       *
 582       * When validating foreign keys the persistent should only check that the associated model
 583       * exists. The validation methods should not be used to check for a change in that relationship.
 584       * The API method setting the attributes on the model should be responsible for that.
 585       * E.g. On a course model, the method validate_categoryid will check that the category exists.
 586       * However, if a course can never be moved outside of its category it would be up to the calling
 587       * code to ensure that the category ID will not be altered.
 588       *
 589       * @return array|true Returns true when the validation passed, or an array of properties with errors.
 590       */
 591      final public function validate() {
 592          global $CFG;
 593  
 594          // Before validate hook.
 595          $this->before_validate();
 596  
 597          // If this object has not been validated yet.
 598          if ($this->validated !== true) {
 599  
 600              $errors = array();
 601              $properties = static::properties_definition();
 602              foreach ($properties as $property => $definition) {
 603  
 604                  // Get the data, bypassing the potential custom getter which could alter the data.
 605                  $value = $this->get($property);
 606  
 607                  // Check if the property is required.
 608                  if ($value === null && static::is_property_required($property)) {
 609                      $errors[$property] = new lang_string('requiredelement', 'form');
 610                      continue;
 611                  }
 612  
 613                  // Check that type of value is respected.
 614                  try {
 615                      if ($definition['type'] === PARAM_BOOL && $value === false) {
 616                          // Validate_param() does not like false with PARAM_BOOL, better to convert it to int.
 617                          $value = 0;
 618                      }
 619                      validate_param($value, $definition['type'], $definition['null']);
 620                  } catch (invalid_parameter_exception $e) {
 621                      $errors[$property] = static::get_property_error_message($property);
 622                      continue;
 623                  }
 624  
 625                  // Check that the value is part of a list of allowed values.
 626                  if (isset($definition['choices']) && !in_array($value, $definition['choices'])) {
 627                      $errors[$property] = static::get_property_error_message($property);
 628                      continue;
 629                  }
 630  
 631                  // Call custom validation method.
 632                  $method = 'validate_' . $property;
 633                  if (method_exists($this, $method)) {
 634  
 635                      // Warn the developers when they are doing something wrong.
 636                      if ($CFG->debugdeveloper) {
 637                          $reflection = new ReflectionMethod($this, $method);
 638                          if (!$reflection->isProtected()) {
 639                              throw new coding_exception('The method ' . get_class($this) . '::'. $method . ' should be protected.');
 640                          }
 641                      }
 642  
 643                      $valid = $this->{$method}($value);
 644                      if ($valid !== true) {
 645                          if (!($valid instanceof lang_string)) {
 646                              throw new coding_exception('Unexpected error message.');
 647                          }
 648                          $errors[$property] = $valid;
 649                          continue;
 650                      }
 651                  }
 652              }
 653  
 654              $this->validated = true;
 655              $this->errors = $errors;
 656          }
 657  
 658          return empty($this->errors) ? true : $this->errors;
 659      }
 660  
 661      /**
 662       * Returns whether or not the model is valid.
 663       *
 664       * @return boolean True when it is.
 665       */
 666      final public function is_valid() {
 667          return $this->validate() === true;
 668      }
 669  
 670      /**
 671       * Returns the validation errors.
 672       *
 673       * @return array
 674       */
 675      final public function get_errors() {
 676          $this->validate();
 677          return $this->errors;
 678      }
 679  
 680      /**
 681       * Extract a record from a row of data.
 682       *
 683       * Most likely used in combination with {@link self::get_sql_fields()}. This method is
 684       * simple enough to be used by non-persistent classes, keep that in mind when modifying it.
 685       *
 686       * e.g. persistent::extract_record($row, 'user'); should work.
 687       *
 688       * @param stdClass $row The row of data.
 689       * @param string $prefix The prefix the data fields are prefixed with, defaults to the table name followed by underscore.
 690       * @return stdClass The extracted data.
 691       */
 692      public static function extract_record($row, $prefix = null) {
 693          if ($prefix === null) {
 694              $prefix = str_replace('_', '', static::TABLE) . '_';
 695          }
 696          $prefixlength = strlen($prefix);
 697  
 698          $data = new stdClass();
 699          foreach ($row as $property => $value) {
 700              if (strpos($property, $prefix) === 0) {
 701                  $propertyname = substr($property, $prefixlength);
 702                  $data->$propertyname = $value;
 703              }
 704          }
 705  
 706          return $data;
 707      }
 708  
 709      /**
 710       * Load a list of records.
 711       *
 712       * @param array $filters Filters to apply.
 713       * @param string $sort Field to sort by.
 714       * @param string $order Sort order.
 715       * @param int $skip Limitstart.
 716       * @param int $limit Number of rows to return.
 717       *
 718       * @return \core_competency\persistent[]
 719       */
 720      public static function get_records($filters = array(), $sort = '', $order = 'ASC', $skip = 0, $limit = 0) {
 721          global $DB;
 722  
 723          $orderby = '';
 724          if (!empty($sort)) {
 725              $orderby = $sort . ' ' . $order;
 726          }
 727  
 728          $records = $DB->get_records(static::TABLE, $filters, $orderby, '*', $skip, $limit);
 729          $instances = array();
 730  
 731          foreach ($records as $record) {
 732              $newrecord = new static(0, $record);
 733              array_push($instances, $newrecord);
 734          }
 735          return $instances;
 736      }
 737  
 738      /**
 739       * Load a single record.
 740       *
 741       * @param array $filters Filters to apply.
 742       * @return false|\core_competency\persistent
 743       */
 744      public static function get_record($filters = array()) {
 745          global $DB;
 746  
 747          $record = $DB->get_record(static::TABLE, $filters);
 748          return $record ? new static(0, $record) : false;
 749      }
 750  
 751      /**
 752       * Load a list of records based on a select query.
 753       *
 754       * @param string $select
 755       * @param array $params
 756       * @param string $sort
 757       * @param string $fields
 758       * @param int $limitfrom
 759       * @param int $limitnum
 760       * @return \core_competency\persistent[]
 761       */
 762      public static function get_records_select($select, $params = null, $sort = '', $fields = '*', $limitfrom = 0, $limitnum = 0) {
 763          global $DB;
 764  
 765          $records = $DB->get_records_select(static::TABLE, $select, $params, $sort, $fields, $limitfrom, $limitnum);
 766  
 767          // We return class instances.
 768          $instances = array();
 769          foreach ($records as $key => $record) {
 770              $instances[$key] = new static(0, $record);
 771          }
 772  
 773          return $instances;
 774  
 775      }
 776  
 777      /**
 778       * Return the list of fields for use in a SELECT clause.
 779       *
 780       * Having the complete list of fields prefixed allows for multiple persistents to be fetched
 781       * in a single query. Use {@link self::extract_record()} to extract the records from the query result.
 782       *
 783       * @param string $alias The alias used for the table.
 784       * @param string $prefix The prefix to use for each field, defaults to the table name followed by underscore.
 785       * @return string The SQL fragment.
 786       */
 787      public static function get_sql_fields($alias, $prefix = null) {
 788          global $CFG;
 789          $fields = array();
 790  
 791          if ($prefix === null) {
 792              $prefix = str_replace('_', '', static::TABLE) . '_';
 793          }
 794  
 795          // Get the properties and move ID to the top.
 796          $properties = static::properties_definition();
 797          $id = $properties['id'];
 798          unset($properties['id']);
 799          $properties = array('id' => $id) + $properties;
 800  
 801          foreach ($properties as $property => $definition) {
 802              $as = $prefix . $property;
 803              $fields[] = $alias . '.' . $property . ' AS ' . $as;
 804  
 805              // Warn developers that the query will not always work.
 806              if ($CFG->debugdeveloper && strlen($as) > 30) {
 807                  throw new coding_exception("The alias '$as' for column '$alias.$property' exceeds 30 characters" .
 808                      " and will therefore not work across all supported databases.");
 809              }
 810          }
 811  
 812          return implode(', ', $fields);
 813      }
 814  
 815      /**
 816       * Count a list of records.
 817       *
 818       * @param array $conditions An array of conditions.
 819       * @return int
 820       */
 821      public static function count_records(array $conditions = array()) {
 822          global $DB;
 823  
 824          $count = $DB->count_records(static::TABLE, $conditions);
 825          return $count;
 826      }
 827  
 828      /**
 829       * Count a list of records.
 830       *
 831       * @param string $select
 832       * @param array $params
 833       * @return int
 834       */
 835      public static function count_records_select($select, $params = null) {
 836          global $DB;
 837  
 838          $count = $DB->count_records_select(static::TABLE, $select, $params);
 839          return $count;
 840      }
 841  
 842      /**
 843       * Check if a record exists by ID.
 844       *
 845       * @param int $id Record ID.
 846       * @return bool
 847       */
 848      public static function record_exists($id) {
 849          global $DB;
 850          return $DB->record_exists(static::TABLE, array('id' => $id));
 851      }
 852  
 853      /**
 854       * Check if a records exists.
 855       *
 856       * @param string $select
 857       * @param array $params
 858       * @return bool
 859       */
 860      public static function record_exists_select($select, array $params = null) {
 861          global $DB;
 862          return $DB->record_exists_select(static::TABLE, $select, $params);
 863      }
 864  
 865  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1