Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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