dwing
view inc/active.class.php @ 186:cfd4d1dcece4
theoretically support OpenID AX with both axschema and schema.openid.net, thank god that no provider has support for this yet somehow
| author | Arpad Borsos <arpad.borsos@googlemail.com> |
|---|---|
| date | Thu Aug 05 12:04:49 2010 +0200 (21 months ago) |
| parents | 2df784358c9f |
| children |
line source
1 <?php
2 /*
3 * dWing - a cms aimed to be as bleeding edge as possible
4 * Copyright (C) 2007-2008 Arpad Borsos <arpad.borsos@googlemail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
20 // TODO: active.php may not be the best file name to house these classes
21 // the autoload handler would miss JSONable for sure
23 /**
24 * Interface for ActiveRecord Classes
25 */
26 interface ActiveRecord
27 {
28 /**
29 * Interfaces to not allow member variables, but please make sure to define
30 * a public $id member.
31 */
32 //public $id;
33 /**
34 * An ActiveRecord class should have a constructor which takes an ID and
35 * fetches the corresponding Object
36 */
37 //public function __construct($aId)
38 /**
39 * Saves the object into the Database
40 * Creates a new transaction and commits it when $aUseTransaction is true
41 */
42 public function save($aUseTransaction = false);
43 /**
44 * Deletes the object from the Database
45 * Creates a new transaction and commits it when $aUseTransaction is true
46 */
47 public function delete($aUseTransaction = false);
48 /**
49 * This function assigns $aData passed in to the object
50 */
51 public function assignData($aData);
52 }
54 /**
55 * Interface for Objects that can be JSON-ified
56 */
57 interface JSONable
58 {
59 /**
60 * Turns the object into either a JSON String when $aEncode is true or
61 * returns a associative array that can be turned into JSON via json_encode()
62 * Also includes ContentProvider Children when $aIncludeChildren is true
63 */
64 public function toJSON($aEncode = true, $aIncludeChildren = false);
65 }
67 /*
68 * TODO:
69 * fetch data on __get and use IFNULL() to not overwrite the record with empty data
70 */
72 /**
73 * Abstract Base implementation of ActiveRecord
74 */
75 /*
76 Used like this:
78 class News extends ActiveItem
79 {
80 //protected $tableName = 'news';
81 protected $primaryKey = 'news_id';
82 protected $definition = array('title' => 'required', 'text' => 'html',
83 'user_id' => 'user', 'time' => 'time', 'fancyurl' => 'value');
84 }
85 class Comment extends ActiveItem
86 {
87 protected $tableName = 'comments';
88 protected $definition = array('text' => 'html', 'user_id' => 'user',
89 'time' => 'time', 'content_id' => 'required', 'content_type' => 'required');
90 }
91 */
92 abstract class ActiveItem implements ActiveRecord, JSONable, ContentItem
93 {
94 private static $statements = array();
95 protected $primaryKey = 'id';
96 protected $data = array();
97 protected $className;
98 protected $tableName;
99 public $id;
101 /**
102 * Until we require PHP5.3, just reimplement this method, returning the
103 * correct ContentType
104 */
105 public static function ContentType()
106 {
107 return 0;
108 }
110 public function __construct($aData = null)
111 {
112 $this->className = get_class($this);
113 if(empty($this->tableName))
114 $this->tableName = strtolower($this->className);
115 $this->tableName = Core::$prefix.$this->tableName;
117 if($aData == null)
118 return;
120 if(is_array($aData))
121 {
122 // primary key set: fetch the old record and overwrite the data with the new one
123 // problem: I want to create Objects from a fetchAll query that don't re-fetch
124 // themselves
125 if(!empty($aData[$this->primaryKey]) || !empty($aData['id']))
126 {
127 if(!empty($aData[$this->primaryKey]))
128 $this->id = (int)$aData[$this->primaryKey];
129 else if(!empty($aData['id']))
130 $this->id = (int)$aData['id'];
131 $this->fetchData();
132 $this->data = array_merge($this->data, $aData);
133 unset($this->data[$this->primaryKey]);
134 }
135 // else: write the data so it can be saved afterwards
136 else
137 {
138 $this->data = $aData;
139 }
140 return;
141 }
142 if(is_numeric($aData)) // We have an Id
143 {
144 $this->id = (int)$aData;
145 $this->fetchData();
146 }
147 else if(in_array('fancyurl', $this->definition)) // We have a fancyurl
148 {
149 $childClass = $this->className;
150 if(empty(self::$statements[$childClass]['fancyurl']))
151 {
152 self::$statements[$childClass]['fancyurl'] =
153 Core::$db->prepare('SELECT * FROM '.$this->tableName.' WHERE
154 fancyurl=:fancyurl;'); // TODO: do not hardcore the fancyurl name
155 }
156 $statement = self::$statements[$childClass]['fancyurl'];
157 $statement->bindValue(':fancyurl', Utils::fancyUrl($aData), PDO::PARAM_STR);
158 $statement->execute();
159 $this->data = $statement->fetch(PDO::FETCH_ASSOC);
160 if(!empty($this->data))
161 {
162 $this->id = $this->data[$this->primaryKey];
163 unset($this->data[$this->primaryKey]);
164 }
165 }
166 }
167 protected function fetchData()
168 {
169 $childClass = $this->className;
170 if(empty(self::$statements[$childClass]['read']))
171 {
172 self::$statements[$childClass]['read'] =
173 Core::$db->prepare('SELECT * FROM '.$this->tableName.' WHERE '.
174 $this->primaryKey.'=:id;');
175 }
176 if(empty($this->data))
177 {
178 $statement = self::$statements[$childClass]['read'];
179 $statement->bindValue(':id', $this->id, PDO::PARAM_INT);
180 $statement->execute();
181 $this->data = $statement->fetch(PDO::FETCH_ASSOC);
182 if(empty($this->data))
183 unset($this->id);
184 }
185 }
186 public function assignData($aData)
187 {
188 $this->data = array_merge($this->data, $aData);
189 }
190 public function __get($aVarName)
191 {
192 $this->fetchData();
193 if(!isset($this->data[$aVarName]))
194 {
195 // TODO: is it really good to hardcore 'user_id' here?
196 if($aVarName == 'user' && isset($this->definition['user_id']))
197 {
198 $this->data['user'] = $user = Users::getUser($this->data['user_id']);
199 return $user;
200 }
201 $singular = Utils::makeSingular($aVarName);
202 if(!Utils::isSingular($aVarName) && class_exists($singular) &&
203 Utils::doesImplement($singular, 'ContentProvider'))
204 {
205 $this->data[$aVarName] = $obj =
206 call_user_func(array($singular, 'getAllFor'), $this);
207 return $obj;
208 }
209 if(Utils::isSingular($aVarName) && class_exists($aVarName) &&
210 Utils::doesImplement($aVarName, 'ContentProviderSingle'))
211 {
212 $this->data[$aVarName] = $obj =
213 call_user_func(array($aVarName, 'getFor'), $this);
214 return $obj;
215 }
216 return null;
217 }
218 return $this->data[$aVarName];
219 }
220 public function __isset($aVarName)
221 {
222 return $this->__get($aVarName) != null;
223 }
224 public function __set($aVarName, $aValue)
225 {
226 if(!isset($this->definition[$aVarName]) && $aVarName != $this->primaryKey)
227 return;
228 if($aVarName == $this->primaryKey)
229 $this->id = $aValue;
230 else
231 $this->data[$aVarName] = $aValue;
232 }
233 public function save($aUseTransaction = false)
234 {
235 if($aUseTransaction)
236 Core::$db->beginTransaction();
237 $childClass = $this->className;
238 if(empty($this->id))
239 {
240 if(empty(self::$statements[$childClass]['create']))
241 {
242 $query = 'INSERT INTO '.$this->tableName.' SET ';
243 $colDefs = array();
244 foreach($this->definition as $column => $options)
245 {
246 $colDefs[] = $column.'=:'.$column;
247 }
248 $query.= implode(', ', $colDefs).';';
249 self::$statements[$childClass]['create'] = Core::$db->prepare($query);
250 }
251 $statement = self::$statements[$childClass]['create'];
252 }
253 else
254 {
255 if(empty(self::$statements[$childClass]['update']))
256 {
257 $query = 'UPDATE '.$this->tableName.' SET ';
258 $colDefs = array();
259 foreach($this->definition as $column => $options)
260 {
261 $colDefs[] = $column.'=:'.$column;
262 //$colDefs[] = $column.'=IFNULL(:'.$column.','.$column.')';
263 }
264 $query.= implode(', ', $colDefs).' WHERE '.$this->primaryKey.'=:id;';
265 self::$statements[$childClass]['update'] = Core::$db->prepare($query);
266 }
267 $statement = self::$statements[$childClass]['update'];
268 $statement->bindValue(':id', $this->id, PDO::PARAM_INT);
269 }
270 foreach($this->definition as $column => $options)
271 {
272 switch($options)
273 {
274 case 'user':
275 if(!isset($this->data[$column]))
276 $this->data[$column] = Core::$user->id;
277 $statement->bindValue(':'.$column, $this->data[$column], PDO::PARAM_INT);
278 break;
279 case 'time':
280 if(!isset($this->data[$column]))
281 $this->data[$column] = time();
282 $statement->bindValue(':'.$column, $this->data[$column], PDO::PARAM_INT);
283 break;
284 case 'html':
285 $this->data[$column] = isset($this->data[$column]) ?
286 Utils::purify($this->data[$column]) : '';
287 $statement->bindValue(':'.$column, $this->data[$column], PDO::PARAM_STR);
288 break;
289 case 'required':
290 if(empty($this->data[$column]))
291 throw new Exception(printf(l10n::_('%s was empty'), $column));
292 $statement->bindValue(':'.$column, $this->data[$column], PDO::PARAM_STR);
293 break;
294 default:
295 if(!isset($this->data[$column]))
296 $this->data[$column] = '';
297 $statement->bindValue(':'.$column, $this->data[$column], PDO::PARAM_STR);
298 }
299 }
300 if(!$statement->execute())
301 throw new Exception($statement->errorInfo[2]);
302 if($aUseTransaction)
303 Core::$db->commit();
304 $return = empty($this->id) ? ($this->id = Core::$db->lastInsertId()) : true;
305 if($aUseTransaction)
306 Core::$db->commit();
307 return $return;
308 }
309 public function delete($aUseTransaction = false)
310 {
311 if($aUseTransaction)
312 Core::$db->beginTransaction();
313 $childClass = $this->className;
314 if(empty(self::$statements[$childClass]['delete']))
315 {
316 self::$statements[$childClass]['delete'] =
317 Core::$db->prepare('DELETE FROM '.$this->tableName.' WHERE '.
318 $this->primaryKey.'=:id;');
319 }
320 $statement = self::$statements[$childClass]['delete'];
321 $statement->bindValue(':id', $this->id, PDO::PARAM_INT);
322 if($return = $statement->execute())
323 {
324 Core::deleteEverythingFor($this);
325 $this->data = array();
326 unset($this->id);
327 }
328 if($aUseTransaction)
329 Core::$db->commit();
330 return $return;
331 }
332 // Maybe this is not the best place for toJSON()
333 public function toJSON($aEncode = true, $aIncludeChildren = false)
334 {
335 // TODO: make use of $aEncode and $aIncludeChildren
336 if(empty($this->id))
337 return 'false';
338 $displayArray = array('id' => $this->id);
339 foreach($this->definition as $column => $option)
340 {
341 if($option == 'user')
342 {
343 $user = Users::getUser($this->data[$column]);
344 $displayArray['user'] = array('id' => $user->id, 'nick' => $user->nick);
345 }
346 else
347 $displayArray[$column] = $this->data[$column];
348 }
349 return json_encode($displayArray);
350 }
351 }
352 ?>
