1<?php 2 3namespace dokuwiki\plugin\bez\mdl; 4 5use dokuwiki\plugin\bez\meta\PermissionDeniedException; 6 7define('BEZ_TABLE_PERMISSION_UNKNOWN', -1); 8define('BEZ_TABLE_PERMISSION_SELECT', 0); 9define('BEZ_TABLE_PERMISSION_INSERT', 1); 10 11abstract class Factory { 12 /** @var Model */ 13 protected $model; 14 15 public function permission() { 16 return BEZ_TABLE_PERMISSION_INSERT; 17 } 18 19 protected function filter_field_map($field) { 20 $table = $this->get_table_view(); 21 if (!$table) { 22 $table = $this->get_table_name(); 23 } 24 25 return "$table.$field"; 26 } 27 28 protected function select_query() { 29 $table = $this->get_table_view(); 30 if (!$table) { 31 $table = $this->get_table_name(); 32 } 33 return "SELECT * FROM $table"; 34 } 35 36 public function get_table_view() { 37 return false; 38 } 39 40 protected function build_where($filters=array()) { 41 $execute = array(); 42 $where_q = array(); 43 foreach ($filters as $filter => $value) { 44 $field = $this->filter_field_map($filter); 45 46 //parser 47 $operator = '='; 48 $function = ''; 49 $function_args = array(); 50 if (is_array($value)) { 51 $operators = array('!=', '<', '>', '<=', '>=', 'LIKE', 'BETWEEN', 'OR'); 52 $functions = array('', 'date'); 53 54 $operator = $value[0]; 55 $function = isset($value[2]) ? $value[2] : ''; 56 57 if (is_array($function)) { 58 $function = $function[0]; 59 $function_args = array_slice($value[2], 1); 60 } 61 62 $value = $value[1]; 63 64 if (!in_array($operator, $operators)) { 65 throw new \Exception('unknown operator: '.$operator); 66 } 67 68 if (!in_array($function, $functions)) { 69 throw new \Exception('unknown function: '.$function); 70 } 71 } 72 73 //builder 74 if ($operator === 'BETWEEN') { 75 if (count($value) < 2) { 76 throw new \Exception('wrong BETWEEN argument. provide two values'); 77 } 78 if ($function !== '') { 79 array_unshift($function_args, $field); 80 $where_q[] = "$function(".implode(',', $function_args).") BETWEEN :${filter}_start AND :${filter}_end"; 81 } else { 82 $where_q[] = "$field BETWEEN :${filter}_start AND :${filter}_end"; 83 } 84 $execute[":${filter}_start"] = $value[0]; 85 $execute[":${filter}_end"] = $value[1]; 86 } elseif ($operator === 'OR') { 87 if (!is_array($value)) { 88 throw new \Exception('$data should be an array'); 89 } 90 91 $where_array = array(); 92 93 foreach ($value as $k => $v) { 94 $exec = ":${filter}_$k"; 95 $where_array[] = "$field = $exec"; 96 $execute[$exec] = $v; 97 } 98 $where_q[] = '('.implode(' OR ', $where_array).')'; 99 100 101 } else { 102 if ($function !== '') { 103 array_unshift($function_args, $field); 104 $where_q[] = "$function(".implode(',', $function_args).") $operator :$filter"; 105 $execute[":$filter"] = $value; 106 } elseif ($value === NULL || $value === '') { 107 $where_q[] = "($field IS NULL OR $field = '')"; 108 } elseif(is_int($value)) { 109 $where_q[] = "$field $operator $value"; 110 } else { 111 $where_q[] = "$field $operator :$filter"; 112 $execute[":$filter"] = $value; 113 } 114 115 } 116 } 117 118 $where = ''; 119 if (count($where_q) > 0) { 120 $where = ' WHERE '.implode(' AND ', $where_q); 121 } 122 return array($where, $execute); 123 } 124 125 public function __construct(Model $model) { 126 $this->model = $model; 127 } 128 129 public function get_all($filters=array(), $orderby='', $defaults=array(), $limit=false) { 130 131 list($where_q, $execute) = $this->build_where($filters); 132 133 $q = $this->select_query() . $where_q; 134 135 if ($orderby != '') { 136 if (is_array($orderby)) $orderby = implode(', ', $orderby); 137 $q .= " ORDER BY $orderby"; 138 } 139 140 if (is_int($limit)) { 141 $q .= " LIMIT $limit"; 142 } 143 144 $sth = $this->model->db->prepare($q); 145 146 $sth->setFetchMode(\PDO::FETCH_CLASS, $this->get_object_class_name(), 147 array($this->model, $defaults)); 148 149 $sth->execute($execute); 150 151 return $sth; 152 } 153 154 public function count($filters=array()) { 155 $table = $this->get_table_view(); 156 if (!$table) { 157 $table = $this->get_table_name(); 158 } 159 160 list($where_q, $execute) = $this->build_where($filters); 161 162 $q = "SELECT COUNT(*) FROM $table " . $where_q; 163 $sth = $this->model->db->prepare($q); 164 $sth->execute($execute); 165 166 $count = $sth->fetchColumn(); 167 return $count; 168 } 169 170 public function exists($id) { 171 $table = $this->get_table_name(); 172 $q = "SELECT id FROM $table WHERE id = ?"; 173 174 $sth = $this->model->db->prepare($q); 175 $sth->execute(array($id)); 176 177 if ($sth->fetch()) { 178 return true; 179 } 180 181 return false; 182 } 183 184 public function get_one($id, $defaults=array()) { 185 $table = $this->get_table_view(); 186 if (!$table) { 187 $table = $this->get_table_name(); 188 } 189 190 $q = $this->select_query()." WHERE $table.id = ?"; 191 192 $sth = $this->model->db->prepare($q); 193 $sth->execute(array($id)); 194 195 $obj = $sth->fetchObject($this->get_object_class_name(), 196 array($this->model, $defaults)); 197 198 if ($obj === false) { 199 throw new \Exception('there is no '.$this->get_table_name().' with id: '.$id); 200 } 201 202 return $obj; 203 } 204 205 public function get_table_name() { 206 $class = (new \ReflectionClass($this))->getShortName(); 207 return lcfirst(str_replace('Factory', '', $class)); 208 } 209 210 public function get_object_class_name() { 211 $class = (new \ReflectionClass($this))->getName(); 212 return str_replace('Factory', '', $class); 213 } 214 215 public function create_object($defaults=array()) { 216 $object_name = $this->get_object_class_name(); 217 218 $obj = new $object_name($this->model, $defaults); 219 return $obj; 220 } 221 222 protected function beginTransaction() { 223 $this->model->sqlite->query('BEGIN TRANSACTION'); 224 } 225 226 protected function commitTransaction() { 227 $this->model->sqlite->query('COMMIT TRANSACTION'); 228 } 229 230 protected function rollbackTransaction() { 231 $this->model->sqlite->query('ROLLBACK'); 232 } 233 234 protected function insert(Entity $obj) { 235 if ($obj->id != NULL) { 236 throw new \Exception('row already saved'); 237 } 238 239 $set = array(); 240 $execute = array(); 241 $columns = array(); 242 foreach ($obj->get_columns() as $column) { 243 if ($obj->$column === null) continue; 244 $set[] = ":$column"; 245 $columns[] = $column; 246 $value = $obj->$column; 247 if ($value === '') { 248 $execute[':'.$column] = null; 249 } else { 250 $execute[':'.$column] = $value; 251 } 252 } 253 254 $query = 'INSERT INTO '.$this->get_table_name().' 255 ('.implode(',', $columns).') 256 VALUES ('.implode(',', $set).')'; 257 258 $sth = $this->model->db->prepare($query); 259 $sth->execute($execute); 260 261 $reflectionClass = new \ReflectionClass($obj); 262 $reflectionProperty = $reflectionClass->getProperty('id'); 263 $reflectionProperty->setAccessible(true); 264 $reflectionProperty->setValue($obj, $this->model->db->lastInsertId()); 265 266 } 267 268 protected function update(Entity $obj) { 269 if ($obj->id == NULL) { 270 throw new \Exception('row not inserted'); 271 } 272 273 $set = array(); 274 $execute = array(':id' => $obj->id); 275 foreach ($obj->get_columns() as $column) { 276 if ($column == 'id') continue; 277 $set[] = "$column=:$column"; 278 $value = $obj->$column; 279 if ($value === '') { 280 $execute[':'.$column] = null; 281 } else { 282 $execute[':'.$column] = $value; 283 } 284 } 285 286 $query = 'UPDATE ' . $this->get_table_name() . 287 ' SET ' . implode(',', $set) . 288 ' WHERE id=:id'; 289 290 $sth = $this->model->db->prepare($query); 291 $sth->execute($execute); 292 } 293 294 public function initial_save(Entity $obj, $data) { 295 $obj->set_data($data); 296 $this->insert($obj); 297 } 298 299 public function update_save(Entity $obj, $data) { 300 $obj->set_data($data); 301 $this->update($obj); 302 } 303 304 public function save(Entity $obj) { 305 if ($obj->id == NULL) { 306 $this->insert($obj); 307 } else { 308 $this->update($obj); 309 } 310 } 311 312 protected function delete_from_db($id) { 313 $q = 'DELETE FROM '.$this->get_table_name().' WHERE id = ?'; 314 $sth = $this->model->db->prepare($q); 315 $sth->execute(array($id)); 316 } 317 318 public function delete(Entity $obj) { 319 if ($obj->acl_of('id') < BEZ_PERMISSION_DELETE) { 320 throw new PermissionDeniedException('cannot delete'); 321 } 322 $this->delete_from_db($obj->id); 323 } 324} 325