2978ccde3a6b534807b29801feaf993c9305c3d9
[moodle.git] / lib / grade / grade_object.php
1 <?php
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  */
26 defined('MOODLE_INTERNAL') || die();
27 /**
28  * An abstract object that holds methods and attributes common to all grade_* objects defined here.
29  * @abstract
30  */
31 abstract class grade_object {
32     public $table;
34     /**
35      * Array of required table fields, must start with 'id'.
36      * @var array $required_fields
37      */
38     public $required_fields = array('id', 'timecreated', 'timemodified', 'hidden');
40     /**
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
44      */
45     public $optional_fields = array();
47     /**
48      * The PK.
49      * @var int $id
50      */
51     public $id;
53     /**
54      * The first time this grade_object was created.
55      * @var int $timecreated
56      */
57     public $timecreated;
59     /**
60      * The last time this grade_object was modified.
61      * @var int $timemodified
62      */
63     public $timemodified;
65     /**
66      * 0 if visible, 1 always hidden or date not visible until
67      * @var int $hidden
68      */
69     var $hidden = 0;
71     /**
72      * Constructor. Optionally (and by default) attempts to fetch corresponding row from DB.
73      * @param array $params an array with required parameters for this grade object.
74      * @param boolean $fetch Whether to fetch corresponding row from DB or not,
75      *        optional fields might not be defined if false used
76      */
77     public function __construct($params=NULL, $fetch=true) {
78         if (!empty($params) and (is_array($params) or is_object($params))) {
79             if ($fetch) {
80                 if ($data = $this->fetch($params)) {
81                     grade_object::set_properties($this, $data);
82                 } else {
83                     grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
84                     grade_object::set_properties($this, $params);
85                 }
87             } else {
88                 grade_object::set_properties($this, $params);
89             }
91         } else {
92             grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
93         }
94     }
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      */
101     public function load_optional_fields() {
102         global $DB;
103         foreach ($this->optional_fields as $field=>$default) {
104             if (property_exists($this, $field)) {
105                 continue;
106             }
107             if (empty($this->id)) {
108                 $this->$field = $default;
109             } else {
110                 $this->$field = $DB->get_field($this->table, $field, array('id', $this->id));
111             }
112         }
113     }
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      */
122     public static function fetch($params) {
123         throw new coding_exception('fetch() method needs to be overridden in each subclass of grade_object');
124     }
126     /**
127      * Finds and returns all grade_object instances based on params.
128      * @static abstract
129      *
130      * @param array $params associative arrays varname=>value
131      * @return array array of grade_object instances or false if none found.
132      */
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     }
137     /**
138      * Factory method - uses the parameters to retrieve matching instance from the DB.
139      * @static final protected
140      * @return mixed object instance or false if not found
141      */
142     protected static function fetch_helper($table, $classname, $params) {
143         if ($instances = grade_object::fetch_all_helper($table, $classname, $params)) {
144             if (count($instances) > 1) {
145                 // we should not tolerate any errors here - problems might appear later
146                 print_error('morethanonerecordinfetch','debug');
147             }
148             return reset($instances);
149         } else {
150             return false;
151         }
152     }
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      */
159     public static function fetch_all_helper($table, $classname, $params) {
160         $instance = new $classname();
162         $classvars = (array)$instance;
163         $params    = (array)$params;
165         $wheresql = array();
166         $newparams = array();
168         foreach ($params as $var=>$value) {
169             if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
170                 continue;
171             }
172             if (is_null($value)) {
173                 $wheresql[] = " $var IS NULL ";
174             } else {
175                 $wheresql[] = " $var = ? ";
176                 $newparams[] = $value;
177             }
178         }
180         if (empty($wheresql)) {
181             $wheresql = '';
182         } else {
183             $wheresql = implode("AND", $wheresql);
184         }
186         global $DB;
187         $rs = $DB->get_recordset_select($table, $wheresql, $newparams);
188         //returning false rather than empty array if nothing found
189         if (!$rs->valid()) {
190              return false;
191         }
193         $result = array();
194         foreach($rs as $data) {
195             $instance = new $classname();
196             grade_object::set_properties($instance, $data);
197             $result[$instance->id] = $instance; 
198         }
199         $rs->close();
200         
201         return $result;
202     }
204     /**
205      * Updates this object in the Database, based on its object variables. ID must be set.
206      * @param string $source from where was the object updated (mod/forum, manual, etc.)
207      * @return boolean success
208      */
209     public function update($source=null) {
210         global $USER, $CFG, $DB;
212         if (empty($this->id)) {
213             debugging('Can not update grade object, no id!');
214             return false;
215         }
217         $data = $this->get_record_data();
219         $DB->update_record($this->table, $data);
221         if (empty($CFG->disablegradehistory)) {
222             unset($data->timecreated);
223             $data->action       = GRADE_HISTORY_UPDATE;
224             $data->oldid        = $this->id;
225             $data->source       = $source;
226             $data->timemodified = time();
227             $data->loggeduser   = $USER->id;
228             $DB->insert_record($this->table.'_history', $data);
229         }
231         $this->notify_changed(false);
232         return true;
233     }
235     /**
236      * Deletes this object from the database.
237      * @param string $source from where was the object deleted (mod/forum, manual, etc.)
238      * @return boolean success
239      */
240     public function delete($source=null) {
241         global $USER, $CFG, $DB;
243         if (empty($this->id)) {
244             debugging('Can not delete grade object, no id!');
245             return false;
246         }
248         $data = $this->get_record_data();
250         if ($DB->delete_records($this->table, array('id'=>$this->id))) {
251             if (empty($CFG->disablegradehistory)) {
252                 unset($data->id);
253                 unset($data->timecreated);
254                 $data->action       = GRADE_HISTORY_DELETE;
255                 $data->oldid        = $this->id;
256                 $data->source       = $source;
257                 $data->timemodified = time();
258                 $data->loggeduser   = $USER->id;
259                 $DB->insert_record($this->table.'_history', $data);
260             }
261             $this->notify_changed(true);
262             return true;
264         } else {
265             return false;
266         }
267     }
269     /**
270      * Returns object with fields and values that are defined in database
271      */
272     public function get_record_data() {
273         $data = new stdClass();
275         foreach ($this as $var=>$value) {
276             if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) {
277                 if (is_object($value) or is_array($value)) {
278                     debugging("Incorrect property '$var' found when inserting grade object");
279                 } else {
280                     $data->$var = $value;
281                 }
282             }
283         }
284         return $data;
285     }
287     /**
288      * Records this object in the Database, sets its id to the returned value, and returns that value.
289      * If successful this function also fetches the new object data from database and stores it
290      * in object properties.
291      * @param string $source from where was the object inserted (mod/forum, manual, etc.)
292      * @return int PK ID if successful, false otherwise
293      */
294     public function insert($source=null) {
295         global $USER, $CFG, $DB;
297         if (!empty($this->id)) {
298             debugging("Grade object already exists!");
299             return false;
300         }
302         $data = $this->get_record_data();
304         $this->id = $DB->insert_record($this->table, $data);
306         // set all object properties from real db data
307         $this->update_from_db();
309         $data = $this->get_record_data();
311         if (empty($CFG->disablegradehistory)) {
312             unset($data->timecreated);
313             $data->action       = GRADE_HISTORY_INSERT;
314             $data->oldid        = $this->id;
315             $data->source       = $source;
316             $data->timemodified = time();
317             $data->loggeduser   = $USER->id;
318             $DB->insert_record($this->table.'_history', $data);
319         }
321         $this->notify_changed(false);
322         return $this->id;
323     }
325     /**
326      * Using this object's id field, fetches the matching record in the DB, and looks at
327      * each variable in turn. If the DB has different data, the db's data is used to update
328      * the object. This is different from the update() function, which acts on the DB record
329      * based on the object.
330      */
331     public function update_from_db() {
332         if (empty($this->id)) {
333             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.");
334             return false;
335         }
336         global $DB;
337         if (!$params = $DB->get_record($this->table, array('id' => $this->id))) {
338             debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!");
339             return false;
340         }
342         grade_object::set_properties($this, $params);
344         return true;
345     }
347     /**
348      * Given an associated array or object, cycles through each key/variable
349      * and assigns the value to the corresponding variable in this object.
350      * @static final
351      */
352     public static function set_properties(&$instance, $params) {
353         $params = (array) $params;
354         foreach ($params as $var => $value) {
355             if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) {
356                 $instance->$var = $value;
357             }
358         }
359     }
361     /**
362      * Called immediately after the object data has been inserted, updated, or
363      * deleted in the database. Default does nothing, can be overridden to
364      * hook in special behaviour.
365      *
366      * @param bool $deleted
367      */
368     function notify_changed($deleted) {
369     }
371     /**
372      * Returns the hidden state of this grade_item
373      * @return boolean hidden state
374      */
375     function is_hidden() {
376         return ($this->hidden == 1 or ($this->hidden != 0 and $this->hidden > time()));
377     }
379     /**
380      * Check grade hidden status. Uses data from both grade item and grade.
381      * @return boolean true if hiddenuntil, false if not
382      */
383     function is_hiddenuntil() {
384         return $this->hidden > 1;
385     }
387     /**
388      * Check grade item hidden status.
389      * @return int 0 means visible, 1 hidden always, timestamp hidden until
390      */
391     function get_hidden() {
392         return $this->hidden;
393     }
395     function set_hidden($hidden, $cascade=false) {
396         $this->hidden = $hidden;
397         $this->update();
398     }