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