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