See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [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 /** 18 * Course completion critieria aggregation 19 * 20 * @package core_completion 21 * @category completion 22 * @copyright 2009 Catalyst IT Ltd 23 * @author Aaron Barnes <aaronb@catalyst.net.nz> 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 30 /** 31 * Trigger for the new data_object api. 32 * 33 * See data_object::__constructor 34 */ 35 define('DATA_OBJECT_FETCH_BY_KEY', 2); 36 37 /** 38 * A data abstraction object that holds methods and attributes 39 * 40 * @package core_completion 41 * @category completion 42 * @copyright 2009 Catalyst IT Ltd 43 * @author Aaron Barnes <aaronb@catalyst.net.nz> 44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 */ 46 abstract class data_object { 47 48 /* @var string Table that the class maps to in the database */ 49 public $table; 50 51 /* @var array Array of required table fields, must start with 'id'. */ 52 public $required_fields = array('id'); 53 54 /** 55 * Array of optional fields with default values - usually long text information that is not always needed. 56 * If you want to create an instance without optional fields use: new data_object($only_required_fields, false); 57 * @var array 58 */ 59 public $optional_fields = array(); 60 61 /* @var Array of unique fields, used in where clauses and constructor */ 62 public $unique_fields = array(); 63 64 /* @var int The primary key */ 65 public $id; 66 67 68 /** 69 * Constructor. Optionally (and by default) attempts to fetch corresponding row from DB. 70 * 71 * If $fetch is not false, there are a few different things that can happen: 72 * - true: 73 * load corresponding row from the database, using $params as the WHERE clause 74 * 75 * - DATA_OBJECT_FETCH_BY_KEY: 76 * load corresponding row from the database, using only the $id in the WHERE clause (if set), 77 * otherwise using the columns listed in $this->unique_fields. 78 * 79 * - array(): 80 * load corresponding row from the database, using the columns listed in this array 81 * in the WHERE clause 82 * 83 * @param array $params required parameters and their values for this data object 84 * @param mixed $fetch if false, do not attempt to fetch from the database, otherwise see notes 85 */ 86 public function __construct($params = null, $fetch = true) { 87 88 if (is_object($params)) { 89 throw new coding_exception('data_object params should be in the form of an array, not an object'); 90 } 91 92 // If no params given, apply defaults for optional fields 93 if (empty($params) || !is_array($params)) { 94 self::set_properties($this, $this->optional_fields); 95 return; 96 } 97 98 // If fetch is false, do not load from database 99 if ($fetch === false) { 100 self::set_properties($this, $params); 101 return; 102 } 103 104 // Compose where clause only from fields in unique_fields 105 if ($fetch === DATA_OBJECT_FETCH_BY_KEY && !empty($this->unique_fields)) { 106 if (empty($params['id'])) { 107 $where = array_intersect_key($params, array_flip($this->unique_fields)); 108 } 109 else { 110 $where = array('id' => $params['id']); 111 } 112 // Compose where clause from given field names 113 } else if (is_array($fetch) && !empty($fetch)) { 114 $where = array_intersect_key($params, array_flip($fetch)); 115 // Use entire params array for where clause 116 } else { 117 $where = $params; 118 } 119 120 // Attempt to load from database 121 if ($data = $this->fetch($where)) { 122 // Apply data from database, then data sent to constructor 123 self::set_properties($this, $data); 124 self::set_properties($this, $params); 125 } else { 126 // Apply defaults for optional fields, then data from constructor 127 self::set_properties($this, $this->optional_fields); 128 self::set_properties($this, $params); 129 } 130 } 131 132 /** 133 * Makes sure all the optional fields are loaded. 134 * 135 * If id present (==instance exists in db) fetches data from db. 136 * Defaults are used for new instances. 137 */ 138 public function load_optional_fields() { 139 global $DB; 140 foreach ($this->optional_fields as $field=>$default) { 141 if (property_exists($this, $field)) { 142 continue; 143 } 144 if (empty($this->id)) { 145 $this->$field = $default; 146 } else { 147 $this->$field = $DB->get_field($this->table, $field, array('id', $this->id)); 148 } 149 } 150 } 151 152 /** 153 * Finds and returns a data_object instance based on params. 154 * 155 * This function MUST be overridden by all deriving classes. 156 * 157 * @param array $params associative arrays varname => value 158 * @throws coding_exception This function MUST be overridden 159 * @return data_object instance of data_object or false if none found. 160 */ 161 public static function fetch($params) { 162 throw new coding_exception('fetch() method needs to be overridden in each subclass of data_object'); 163 } 164 165 /** 166 * Finds and returns all data_object instances based on params. 167 * 168 * This function MUST be overridden by all deriving classes. 169 * 170 * @param array $params associative arrays varname => value 171 * @throws coding_exception This function MUST be overridden 172 * @return array array of data_object instances or false if none found. 173 */ 174 public static function fetch_all($params) { 175 throw new coding_exception('fetch_all() method needs to be overridden in each subclass of data_object'); 176 } 177 178 /** 179 * Factory method - uses the parameters to retrieve matching instance from the DB. 180 * 181 * @final 182 * @param string $table The table name to fetch from 183 * @param string $classname The class that you want the result instantiated as 184 * @param array $params Any params required to select the desired row 185 * @return object Instance of $classname or false. 186 */ 187 protected static function fetch_helper($table, $classname, $params) { 188 if ($instances = self::fetch_all_helper($table, $classname, $params)) { 189 if (count($instances) > 1) { 190 // we should not tolerate any errors here - problems might appear later 191 throw new \moodle_exception('morethanonerecordinfetch', 'debug'); 192 } 193 return reset($instances); 194 } else { 195 return false; 196 } 197 } 198 199 /** 200 * Factory method - uses the parameters to retrieve all matching instances from the DB. 201 * 202 * @final 203 * @param string $table The table name to fetch from 204 * @param string $classname The class that you want the result instantiated as 205 * @param array $params Any params required to select the desired row 206 * @return mixed array of object instances or false if not found 207 */ 208 public static function fetch_all_helper($table, $classname, $params) { 209 $instance = new $classname(); 210 211 $classvars = (array)$instance; 212 $params = (array)$params; 213 214 $wheresql = array(); 215 216 $dbparams = array(); 217 foreach ($params as $var=>$value) { 218 if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) { 219 continue; 220 } 221 if (is_null($value)) { 222 $wheresql[] = " $var IS NULL "; 223 } else { 224 $wheresql[] = " $var = ? "; 225 $dbparams[] = $value; 226 } 227 } 228 229 if (empty($wheresql)) { 230 $wheresql = ''; 231 } else { 232 $wheresql = implode("AND", $wheresql); 233 } 234 235 global $DB; 236 if ($datas = $DB->get_records_select($table, $wheresql, $dbparams)) { 237 238 $result = array(); 239 foreach($datas as $data) { 240 $instance = new $classname(); 241 self::set_properties($instance, $data); 242 $result[$instance->id] = $instance; 243 } 244 return $result; 245 246 } else { 247 248 return false; 249 } 250 } 251 252 /** 253 * Updates this object in the Database, based on its object variables. ID must be set. 254 * 255 * @return bool success 256 */ 257 public function update() { 258 global $DB; 259 260 if (empty($this->id)) { 261 debugging('Can not update data object, no id!'); 262 return false; 263 } 264 265 $data = $this->get_record_data(); 266 267 $DB->update_record($this->table, $data); 268 269 $this->notify_changed(false); 270 return true; 271 } 272 273 /** 274 * Deletes this object from the database. 275 * 276 * @return bool success 277 */ 278 public function delete() { 279 global $DB; 280 281 if (empty($this->id)) { 282 debugging('Can not delete data object, no id!'); 283 return false; 284 } 285 286 $data = $this->get_record_data(); 287 288 if ($DB->delete_records($this->table, array('id'=>$this->id))) { 289 $this->notify_changed(true); 290 return true; 291 292 } else { 293 return false; 294 } 295 } 296 297 /** 298 * Returns object with fields and values that are defined in database 299 * 300 * @return stdClass 301 */ 302 public function get_record_data() { 303 $data = new stdClass(); 304 305 foreach ($this as $var=>$value) { 306 if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) { 307 if (is_object($value) or is_array($value)) { 308 debugging("Incorrect property '$var' found when inserting data object"); 309 } else { 310 $data->$var = $value; 311 } 312 } 313 } 314 return $data; 315 } 316 317 /** 318 * Records this object in the Database, sets its id to the returned value, and returns that value. 319 * If successful this function also fetches the new object data from database and stores it 320 * in object properties. 321 * 322 * @return int PK ID if successful, false otherwise 323 */ 324 public function insert() { 325 global $DB; 326 327 if (!empty($this->id)) { 328 debugging("Data object already exists!"); 329 return false; 330 } 331 332 $data = $this->get_record_data(); 333 334 $this->id = $DB->insert_record($this->table, $data); 335 336 // set all object properties from real db data 337 $this->update_from_db(); 338 339 $this->notify_changed(false); 340 return $this->id; 341 } 342 343 /** 344 * Using this object's id field, fetches the matching record in the DB, and looks at 345 * each variable in turn. If the DB has different data, the db's data is used to update 346 * the object. This is different from the update() function, which acts on the DB record 347 * based on the object. 348 * 349 * @return bool True for success, false otherwise. 350 */ 351 public function update_from_db() { 352 if (empty($this->id)) { 353 debugging("The object could not be used in its state to retrieve a matching record from the DB, because its id field is not set."); 354 return false; 355 } 356 global $DB; 357 if (!$params = $DB->get_record($this->table, array('id' => $this->id))) { 358 debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!"); 359 return false; 360 } 361 362 self::set_properties($this, $params); 363 364 return true; 365 } 366 367 /** 368 * Given an associated array or object, cycles through each key/variable 369 * and assigns the value to the corresponding variable in this object. 370 * 371 * @final 372 * @param data_object $instance 373 * @param array $params 374 */ 375 public static function set_properties(&$instance, $params) { 376 $params = (array) $params; 377 foreach ($params as $var => $value) { 378 if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) { 379 $instance->$var = $value; 380 } 381 } 382 } 383 384 /** 385 * Called immediately after the object data has been inserted, updated, or 386 * deleted in the database. Default does nothing, can be overridden to 387 * hook in special behaviour. 388 * 389 * @param bool $deleted Set this to true if it has been deleted. 390 */ 391 public function notify_changed($deleted) { 392 } 393 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body