dwing

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