gradebook MDL-25713 now closing the recordset even when theres a problem
[moodle.git] / lib / grade / grade_object.php
CommitLineData
4a0e2e63 1<?php
8a31e65c 2
7ad5a627
PS
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17/**
18 * Definitions of grade object class
19 *
20 * @package core
21 * @subpackage grade
22 * @copyright 2006 Nicolas Connault
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
8a31e65c 25
7ad5a627 26defined('MOODLE_INTERNAL') || die();
8a31e65c 27/**
28 * An abstract object that holds methods and attributes common to all grade_* objects defined here.
29 * @abstract
30 */
da3801e8 31abstract class grade_object {
32 public $table;
33
8a31e65c 34 /**
3f2b0c8a 35 * Array of required table fields, must start with 'id'.
36 * @var array $required_fields
8a31e65c 37 */
a25bb902 38 public $required_fields = array('id', 'timecreated', 'timemodified', 'hidden');
8a31e65c 39
40 /**
3f2b0c8a 41 * Array of optional fields with default values - usually long text information that is not always needed.
42 * If you want to create an instance without optional fields use: new grade_object($only_required_fields, false);
43 * @var array $optional_fields
8a31e65c 44 */
da3801e8 45 public $optional_fields = array();
772ddfbf 46
8a31e65c 47 /**
48 * The PK.
772ddfbf 49 * @var int $id
8a31e65c 50 */
da3801e8 51 public $id;
772ddfbf 52
8a31e65c 53 /**
3f2b0c8a 54 * The first time this grade_object was created.
8a31e65c 55 * @var int $timecreated
56 */
da3801e8 57 public $timecreated;
772ddfbf 58
8a31e65c 59 /**
3f2b0c8a 60 * The last time this grade_object was modified.
8a31e65c 61 * @var int $timemodified
62 */
da3801e8 63 public $timemodified;
772ddfbf 64
a25bb902
AD
65 /**
66 * 0 if visible, 1 always hidden or date not visible until
67 * @var int $hidden
68 */
69 var $hidden = 0;
70
8a31e65c 71 /**
3058964f 72 * Constructor. Optionally (and by default) attempts to fetch corresponding row from DB.
f92dcad8 73 * @param array $params an array with required parameters for this grade object.
9f01047a 74 * @param boolean $fetch Whether to fetch corresponding row from DB or not,
75 * optional fields might not be defined if false used
772ddfbf 76 */
da3801e8 77 public function __construct($params=NULL, $fetch=true) {
f92dcad8 78 if (!empty($params) and (is_array($params) or is_object($params))) {
3ab3dfd5 79 if ($fetch) {
80 if ($data = $this->fetch($params)) {
f3ac8eb4 81 grade_object::set_properties($this, $data);
3ab3dfd5 82 } else {
f3ac8eb4 83 grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
84 grade_object::set_properties($this, $params);
3ab3dfd5 85 }
f92dcad8 86
87 } else {
f3ac8eb4 88 grade_object::set_properties($this, $params);
f92dcad8 89 }
3ab3dfd5 90
91 } else {
f3ac8eb4 92 grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
f92dcad8 93 }
94 }
95
3f2b0c8a 96 /**
97 * Makes sure all the optional fields are loaded.
98 * If id present (==instance exists in db) fetches data from db.
99 * Defaults are used for new instances.
100 */
da3801e8 101 public function load_optional_fields() {
102 global $DB;
3f2b0c8a 103 foreach ($this->optional_fields as $field=>$default) {
22a9b6d8 104 if (property_exists($this, $field)) {
3f2b0c8a 105 continue;
106 }
107 if (empty($this->id)) {
108 $this->$field = $default;
109 } else {
da3801e8 110 $this->$field = $DB->get_field($this->table, $field, array('id', $this->id));
3f2b0c8a 111 }
112 }
113 }
114
f92dcad8 115 /**
116 * Finds and returns a grade_object instance based on params.
117 * @static abstract
118 *
119 * @param array $params associative arrays varname=>value
120 * @return object grade_object instance or false if none found.
121 */
7c109ea3
PS
122 public static function fetch($params) {
123 throw new coding_exception('fetch() method needs to be overridden in each subclass of grade_object');
124 }
f92dcad8 125
126 /**
127 * Finds and returns all grade_object instances based on params.
128 * @static abstract
129 *
130 * @param array $params associative arrays varname=>value
f7d515b6 131 * @return array array of grade_object instances or false if none found.
f92dcad8 132 */
7c109ea3
PS
133 public static function fetch_all($params) {
134 throw new coding_exception('fetch_all() method needs to be overridden in each subclass of grade_object');
135 }
f92dcad8 136
137 /**
138 * Factory method - uses the parameters to retrieve matching instance from the DB.
139 * @static final protected
1f0e4921 140 * @return mixed object instance or false if not found
f92dcad8 141 */
da3801e8 142 protected static function fetch_helper($table, $classname, $params) {
f3ac8eb4 143 if ($instances = grade_object::fetch_all_helper($table, $classname, $params)) {
f92dcad8 144 if (count($instances) > 1) {
514a3467 145 // we should not tolerate any errors here - problems might appear later
473e3682 146 print_error('morethanonerecordinfetch','debug');
3058964f 147 }
514a3467 148 return reset($instances);
f92dcad8 149 } else {
150 return false;
151 }
152 }
153
154 /**
155 * Factory method - uses the parameters to retrieve all matching instances from the DB.
156 * @static final protected
157 * @return mixed array of object instances or false if not found
158 */
d24832f9 159 public static function fetch_all_helper($table, $classname, $params) {
f92dcad8 160 $instance = new $classname();
161
162 $classvars = (array)$instance;
163 $params = (array)$params;
164
165 $wheresql = array();
b16b5857 166 $newparams = array();
f92dcad8 167
f92dcad8 168 foreach ($params as $var=>$value) {
3f2b0c8a 169 if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
f92dcad8 170 continue;
171 }
172 if (is_null($value)) {
173 $wheresql[] = " $var IS NULL ";
174 } else {
5b0af8c5 175 $wheresql[] = " $var = ? ";
b16b5857 176 $newparams[] = $value;
f92dcad8 177 }
178 }
179
180 if (empty($wheresql)) {
181 $wheresql = '';
182 } else {
183 $wheresql = implode("AND", $wheresql);
184 }
185
da3801e8 186 global $DB;
2f9ea7d7 187 $rs = $DB->get_recordset_select($table, $wheresql, $newparams);
188 //returning false rather than empty array if nothing found
189 if (!$rs->valid()) {
fc61acce 190 $rs->close();
191 return false;
2f9ea7d7 192 }
4a0e2e63 193
2f9ea7d7 194 $result = array();
195 foreach($rs as $data) {
196 $instance = new $classname();
197 grade_object::set_properties($instance, $data);
198 $result[$instance->id] = $instance;
772ddfbf 199 }
2f9ea7d7 200 $rs->close();
201
202 return $result;
8a31e65c 203 }
772ddfbf 204
8a31e65c 205 /**
206 * Updates this object in the Database, based on its object variables. ID must be set.
aaff71da 207 * @param string $source from where was the object updated (mod/forum, manual, etc.)
208 * @return boolean success
8a31e65c 209 */
da3801e8 210 public function update($source=null) {
211 global $USER, $CFG, $DB;
1ee0df06 212
213 if (empty($this->id)) {
214 debugging('Can not update grade object, no id!');
215 return false;
216 }
f744267a 217
89a5f827 218 $data = $this->get_record_data();
b3ac6c3e 219
2a7eff41 220 $DB->update_record($this->table, $data);
aaff71da 221
1ee0df06 222 if (empty($CFG->disablegradehistory)) {
223 unset($data->timecreated);
224 $data->action = GRADE_HISTORY_UPDATE;
225 $data->oldid = $this->id;
226 $data->source = $source;
227 $data->timemodified = time();
f64e29e1 228 $data->loggeduser = $USER->id;
5b0af8c5 229 $DB->insert_record($this->table.'_history', $data);
1ee0df06 230 }
aaff71da 231
4e781c7b 232 $this->notify_changed(false);
aaff71da 233 return true;
8a31e65c 234 }
235
236 /**
237 * Deletes this object from the database.
aaff71da 238 * @param string $source from where was the object deleted (mod/forum, manual, etc.)
239 * @return boolean success
8a31e65c 240 */
da3801e8 241 public function delete($source=null) {
242 global $USER, $CFG, $DB;
aaff71da 243
1ee0df06 244 if (empty($this->id)) {
245 debugging('Can not delete grade object, no id!');
246 return false;
aaff71da 247 }
248
1ee0df06 249 $data = $this->get_record_data();
250
f67cab32 251 if ($DB->delete_records($this->table, array('id'=>$this->id))) {
1ee0df06 252 if (empty($CFG->disablegradehistory)) {
253 unset($data->id);
254 unset($data->timecreated);
255 $data->action = GRADE_HISTORY_DELETE;
256 $data->oldid = $this->id;
257 $data->source = $source;
258 $data->timemodified = time();
bfc7353d 259 $data->loggeduser = $USER->id;
5b0af8c5 260 $DB->insert_record($this->table.'_history', $data);
aaff71da 261 }
4e781c7b 262 $this->notify_changed(true);
aaff71da 263 return true;
264
265 } else {
266 return false;
267 }
8a31e65c 268 }
772ddfbf 269
89a5f827 270 /**
271 * Returns object with fields and values that are defined in database
272 */
da3801e8 273 public function get_record_data() {
365a5941 274 $data = new stdClass();
294ce987 275
89a5f827 276 foreach ($this as $var=>$value) {
277 if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) {
278 if (is_object($value) or is_array($value)) {
279 debugging("Incorrect property '$var' found when inserting grade object");
280 } else {
281 $data->$var = $value;
282 }
283 }
284 }
285 return $data;
286 }
287
8a31e65c 288 /**
289 * Records this object in the Database, sets its id to the returned value, and returns that value.
9b7e5a37 290 * If successful this function also fetches the new object data from database and stores it
291 * in object properties.
aaff71da 292 * @param string $source from where was the object inserted (mod/forum, manual, etc.)
8a31e65c 293 * @return int PK ID if successful, false otherwise
294 */
da3801e8 295 public function insert($source=null) {
296 global $USER, $CFG, $DB;
f744267a 297
de420c11 298 if (!empty($this->id)) {
d9907766 299 debugging("Grade object already exists!");
300 return false;
b55997c1 301 }
302
89a5f827 303 $data = $this->get_record_data();
b3ac6c3e 304
2a7eff41 305 $this->id = $DB->insert_record($this->table, $data);
9b7e5a37 306
307 // set all object properties from real db data
308 $this->update_from_db();
309
1ee0df06 310 $data = $this->get_record_data();
311
312 if (empty($CFG->disablegradehistory)) {
313 unset($data->timecreated);
314 $data->action = GRADE_HISTORY_INSERT;
315 $data->oldid = $this->id;
316 $data->source = $source;
317 $data->timemodified = time();
bfc7353d 318 $data->loggeduser = $USER->id;
5b0af8c5 319 $DB->insert_record($this->table.'_history', $data);
1ee0df06 320 }
aaff71da 321
4e781c7b 322 $this->notify_changed(false);
9b7e5a37 323 return $this->id;
8a31e65c 324 }
2c72af1f 325
326 /**
327 * Using this object's id field, fetches the matching record in the DB, and looks at
328 * each variable in turn. If the DB has different data, the db's data is used to update
329 * the object. This is different from the update() function, which acts on the DB record
330 * based on the object.
331 */
da3801e8 332 public function update_from_db() {
2c72af1f 333 if (empty($this->id)) {
ab53054f 334 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.");
2c72af1f 335 return false;
2c72af1f 336 }
da3801e8 337 global $DB;
338 if (!$params = $DB->get_record($this->table, array('id' => $this->id))) {
b3ac6c3e 339 debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!");
9b7e5a37 340 return false;
341 }
342
f3ac8eb4 343 grade_object::set_properties($this, $params);
9b7e5a37 344
2c72af1f 345 return true;
346 }
772ddfbf 347
3058964f 348 /**
349 * Given an associated array or object, cycles through each key/variable
350 * and assigns the value to the corresponding variable in this object.
f92dcad8 351 * @static final
3058964f 352 */
da3801e8 353 public static function set_properties(&$instance, $params) {
76144765 354 $params = (array) $params;
f92dcad8 355 foreach ($params as $var => $value) {
3f2b0c8a 356 if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) {
f92dcad8 357 $instance->$var = $value;
3058964f 358 }
772ddfbf 359 }
3058964f 360 }
4e781c7b 361
362 /**
4a0e2e63
PS
363 * Called immediately after the object data has been inserted, updated, or
364 * deleted in the database. Default does nothing, can be overridden to
4e781c7b 365 * hook in special behaviour.
366 *
367 * @param bool $deleted
368 */
369 function notify_changed($deleted) {
370 }
a25bb902
AD
371
372 /**
373 * Returns the hidden state of this grade_item
374 * @return boolean hidden state
375 */
376 function is_hidden() {
377 return ($this->hidden == 1 or ($this->hidden != 0 and $this->hidden > time()));
378 }
379
380 /**
381 * Check grade hidden status. Uses data from both grade item and grade.
382 * @return boolean true if hiddenuntil, false if not
383 */
384 function is_hiddenuntil() {
385 return $this->hidden > 1;
386 }
387
388 /**
389 * Check grade item hidden status.
390 * @return int 0 means visible, 1 hidden always, timestamp hidden until
391 */
392 function get_hidden() {
393 return $this->hidden;
394 }
395
396 function set_hidden($hidden, $cascade=false) {
397 $this->hidden = $hidden;
398 $this->update();
399 }
8a31e65c 400}