Merge branch 'install_master' of git://github.com/amosbot/moodle
[moodle.git] / lib / completion / data_object.php
CommitLineData
2be4d090
MD
1<?php
2
3///////////////////////////////////////////////////////////////////////////
4// //
5// NOTICE OF COPYRIGHT //
6// //
7// Moodle - Modular Object-Oriented Dynamic Learning Environment //
8// http://moodle.com //
9// //
10// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11// //
12// This program is free software; you can redistribute it and/or modify //
13// it under the terms of the GNU General Public License as published by //
14// the Free Software Foundation; either version 2 of the License, or //
15// (at your option) any later version. //
16// //
17// This program is distributed in the hope that it will be useful, //
18// but WITHOUT ANY WARRANTY; without even the implied warranty of //
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20// GNU General Public License for more details: //
21// //
22// http://www.gnu.org/copyleft/gpl.html //
23// //
24///////////////////////////////////////////////////////////////////////////
25
26/**
27 * A data abstraction object that holds methods and attributes
28 * @abstract
29 */
30abstract class data_object {
31 /**
32 * Table that the class maps to in the database
33 * @var string $table
34 */
35 public $table;
36
37 /**
38 * Array of required table fields, must start with 'id'.
39 * @var array $required_fields
40 */
41 public $required_fields = array('id');
42
43 /**
44 * Array of optional fields with default values - usually long text information that is not always needed.
45 * If you want to create an instance without optional fields use: new data_object($only_required_fields, false);
46 * @var array $optional_fields
47 */
48 public $optional_fields = array();
49
50 /**
51 * The PK.
52 * @var int $id
53 */
54 public $id;
55
56 /**
57 * Constructor. Optionally (and by default) attempts to fetch corresponding row from DB.
58 * @param array $params an array with required parameters for this data object.
59 * @param boolean $fetch Whether to fetch corresponding row from DB or not,
60 * optional fields might not be defined if false used
61 */
62 public function __construct($params=NULL, $fetch=true) {
63 if (!empty($params) and (is_array($params) or is_object($params))) {
64 if ($fetch) {
65 if ($data = $this->fetch($params)) {
66 self::set_properties($this, $data);
67 } else {
68 self::set_properties($this, $this->optional_fields);//apply defaults for optional fields
69 self::set_properties($this, $params);
70 }
71
72 } else {
73 self::set_properties($this, $params);
74 }
75
76 } else {
77 self::set_properties($this, $this->optional_fields);//apply defaults for optional fields
78 }
79 }
80
81 /**
82 * Makes sure all the optional fields are loaded.
83 * If id present (==instance exists in db) fetches data from db.
84 * Defaults are used for new instances.
85 */
86 public function load_optional_fields() {
87 global $DB;
88 foreach ($this->optional_fields as $field=>$default) {
4cc977a6 89 if (property_exists($this, $field)) {
2be4d090
MD
90 continue;
91 }
92 if (empty($this->id)) {
93 $this->$field = $default;
94 } else {
95 $this->$field = $DB->get_field($this->table, $field, array('id', $this->id));
96 }
97 }
98 }
99
100 /**
101 * Finds and returns a data_object instance based on params.
102 * @static abstract
103 *
104 * @param array $params associative arrays varname=>value
105 * @return object data_object instance or false if none found.
106 */
7c109ea3
PS
107 public static function fetch($params) {
108 throw new coding_exception('fetch() method needs to be overridden in each subclass of data_object');
109 }
2be4d090
MD
110
111 /**
112 * Finds and returns all data_object instances based on params.
113 *
114 * @param array $params associative arrays varname=>value
7c109ea3 115 * @return array array of data_object instances or false if none found.
2be4d090 116 */
7c109ea3
PS
117 public static function fetch_all($params) {
118 throw new coding_exception('fetch_all() method needs to be overridden in each subclass of data_object');
119 }
2be4d090
MD
120
121 /**
122 * Factory method - uses the parameters to retrieve matching instance from the DB.
123 * @static final protected
124 * @return mixed object instance or false if not found
125 */
126 protected static function fetch_helper($table, $classname, $params) {
127 if ($instances = self::fetch_all_helper($table, $classname, $params)) {
128 if (count($instances) > 1) {
129 // we should not tolerate any errors here - problems might appear later
130 print_error('morethanonerecordinfetch','debug');
131 }
132 return reset($instances);
133 } else {
134 return false;
135 }
136 }
137
138 /**
139 * Factory method - uses the parameters to retrieve all matching instances from the DB.
140 * @static final protected
141 * @return mixed array of object instances or false if not found
142 */
143 public static function fetch_all_helper($table, $classname, $params) {
144 $instance = new $classname();
145
146 $classvars = (array)$instance;
147 $params = (array)$params;
148
149 $wheresql = array();
150
151 foreach ($params as $var=>$value) {
152 if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
153 continue;
154 }
155 if (is_null($value)) {
156 $wheresql[] = " $var IS NULL ";
157 } else {
158 $wheresql[] = " $var = ? ";
159 $params[] = $value;
160 }
161 }
162
163 if (empty($wheresql)) {
164 $wheresql = '';
165 } else {
166 $wheresql = implode("AND", $wheresql);
167 }
168
169 global $DB;
170 if ($datas = $DB->get_records_select($table, $wheresql, $params)) {
6142a4a5 171
2be4d090
MD
172 $result = array();
173 foreach($datas as $data) {
174 $instance = new $classname();
175 self::set_properties($instance, $data);
176 $result[$instance->id] = $instance;
177 }
178 return $result;
179
180 } else {
6142a4a5 181
2be4d090
MD
182 return false;
183 }
184 }
185
186 /**
187 * Updates this object in the Database, based on its object variables. ID must be set.
188 * @return boolean success
189 */
190 public function update() {
191 global $DB;
192
193 if (empty($this->id)) {
194 debugging('Can not update data object, no id!');
195 return false;
196 }
197
198 $data = $this->get_record_data();
199
200 $DB->update_record($this->table, $data);
201
202 $this->notify_changed(false);
203 return true;
204 }
205
206 /**
207 * Deletes this object from the database.
208 * @return boolean success
209 */
210 public function delete() {
211 global $DB;
212
213 if (empty($this->id)) {
214 debugging('Can not delete data object, no id!');
215 return false;
216 }
217
218 $data = $this->get_record_data();
219
220 if ($DB->delete_records($this->table, array('id'=>$this->id))) {
221 $this->notify_changed(true);
222 return true;
223
224 } else {
225 return false;
226 }
227 }
228
229 /**
230 * Returns object with fields and values that are defined in database
231 */
232 public function get_record_data() {
365a5941 233 $data = new stdClass();
2be4d090
MD
234
235 foreach ($this as $var=>$value) {
236 if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) {
237 if (is_object($value) or is_array($value)) {
238 debugging("Incorrect property '$var' found when inserting data object");
239 } else {
240 $data->$var = $value;
241 }
242 }
243 }
244 return $data;
245 }
246
247 /**
248 * Records this object in the Database, sets its id to the returned value, and returns that value.
249 * If successful this function also fetches the new object data from database and stores it
250 * in object properties.
251 * @return int PK ID if successful, false otherwise
252 */
253 public function insert() {
254 global $DB;
255
256 if (!empty($this->id)) {
257 debugging("Data object already exists!");
258 return false;
259 }
260
261 $data = $this->get_record_data();
262
263 $this->id = $DB->insert_record($this->table, $data);
264
265 // set all object properties from real db data
266 $this->update_from_db();
267
268 $this->notify_changed(false);
269 return $this->id;
270 }
271
272 /**
273 * Using this object's id field, fetches the matching record in the DB, and looks at
274 * each variable in turn. If the DB has different data, the db's data is used to update
275 * the object. This is different from the update() function, which acts on the DB record
276 * based on the object.
277 */
278 public function update_from_db() {
279 if (empty($this->id)) {
280 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.");
281 return false;
282 }
283 global $DB;
284 if (!$params = $DB->get_record($this->table, array('id' => $this->id))) {
285 debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!");
286 return false;
287 }
288
289 self::set_properties($this, $params);
290
291 return true;
292 }
293
294 /**
295 * Given an associated array or object, cycles through each key/variable
296 * and assigns the value to the corresponding variable in this object.
297 * @static final
298 */
299 public static function set_properties(&$instance, $params) {
300 $params = (array) $params;
301 foreach ($params as $var => $value) {
302 if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) {
303 $instance->$var = $value;
304 }
305 }
306 }
307
308 /**
6142a4a5
PS
309 * Called immediately after the object data has been inserted, updated, or
310 * deleted in the database. Default does nothing, can be overridden to
2be4d090
MD
311 * hook in special behaviour.
312 *
313 * @param bool $deleted
314 */
315 function notify_changed($deleted) {
316 }
317}