xref: /plugin/bez/mdl/Factory.php (revision ff14b1073c2dab2f863cab3b8baf8b1a01f7993a)
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                } else {
99                    $where_q[] = "$field $operator :$filter";
100                }
101                $execute[":$filter"] = $value;
102            }
103		}
104
105		$where = '';
106		if (count($where_q) > 0) {
107			$where = ' WHERE '.implode(' AND ', $where_q);
108		}
109		return array($where, $execute);
110	}
111
112	public function __construct(Model $model) {
113		$this->model = $model;
114	}
115
116	public function acl_static($field) {
117        return $this->model->acl->check_static_field($this->get_table_name(), $field);
118    }
119
120    //chek acl
121    public function get_all($filters=array(), $orderby='', $desc=true, $defaults=array(), $limit=false) {
122
123        list($where_q, $execute) = $this->build_where($filters);
124
125		$q = $this->select_query() . $where_q;
126
127		if ($orderby != '') {
128		    $fields = call_user_func(array($this->get_object_class_name(), 'get_columns'));
129		    if (!in_array($orderby, $fields)) {
130		        throw new \Exception('unknown field '.$orderby);
131            }
132		    $q .= " ORDER BY $orderby";
133		    if ($desc) {
134		        $q .= " DESC";
135            }
136        }
137
138        if (is_int($limit)) {
139		    $q .= " LIMIT $limit";
140        }
141
142		$sth = $this->model->db->prepare($q);
143
144        $sth->setFetchMode(\PDO::FETCH_CLASS, $this->get_object_class_name(),
145                           array($this->model, $defaults));
146
147		$sth->execute($execute);
148
149		return $sth;
150    }
151
152    public function count($filters=array()) {
153        $table = $this->get_table_view();
154        if (!$table) {
155            $table = $this->get_table_name();
156        }
157        if ($this->acl_static('id') < BEZ_PERMISSION_VIEW) {
158            throw new PermissionDeniedException();
159        }
160
161        list($where_q, $execute) = $this->build_where($filters);
162
163        $q = "SELECT COUNT(*) FROM $table " . $where_q;
164        $sth = $this->model->db->prepare($q);
165        $sth->execute($execute);
166
167        $count = $sth->fetchColumn();
168        return $count;
169    }
170
171    public function get_one($id, $defaults=array()) {
172        $table = $this->get_table_view();
173        if (!$table) {
174            $table = $this->get_table_name();
175        }
176
177		$q = $this->select_query()." WHERE $table.id = ?";
178
179		$sth = $this->model->db->prepare($q);
180		$sth->execute(array($id));
181
182		$obj = $sth->fetchObject($this->get_object_class_name(),
183					array($this->model, $defaults));
184
185        if ($obj === false) {
186            throw new \Exception('there is no '.$this->get_table_name().' with id: '.$id);
187        }
188
189		return $obj;
190	}
191
192	public function get_table_name() {
193        $class = (new \ReflectionClass($this))->getShortName();
194        return lcfirst(str_replace('Factory', '', $class));
195	}
196
197    public function get_object_class_name() {
198        $class = (new \ReflectionClass($this))->getName();
199        return str_replace('Factory', '', $class);
200    }
201//
202//    public function get_table_singular() {
203//        $table = $this->get_table_name();
204//        $singular = substr($table, 0, -1);
205//        return $singular;
206//    }
207//
208//    private function get_singular_object_name() {
209//        return ucfirst($this->get_table_singular());
210//    }
211//
212//    private function get_object_class_name() {
213//        return 'BEZ_mdl_'.$this->get_singular_object_name();
214//    }
215
216//    private function get_dummy_object_class_name() {
217//        return 'BEZ_mdl_Dummy_'.$this->get_singular_object_name();
218//    }
219
220    public function create_object($defaults=array()) {
221        $object_name = $this->get_object_class_name();
222
223		$obj = new $object_name($this->model, $defaults);
224		return $obj;
225	}
226
227	protected function beginTransaction() {
228        $this->model->sqlite->query('BEGIN TRANSACTION');
229    }
230
231    protected function commitTransaction() {
232        $this->model->sqlite->query('COMMIT TRANSACTION');
233    }
234
235    protected function rollbackTransaction() {
236        $this->model->sqlite->query('ROLLBACK');
237    }
238
239//    public function get_dummy_object() {
240//        if ($this->dummy_object === NULL) {
241//            $dummy_object_name = $this->get_dummy_object_class_name();
242//            $this->dummy_object = new $dummy_object_name($this->model);
243//        }
244//        return $this->dummy_object;
245//	}
246
247	public function save(Entity $obj) {
248        //if user can change id, he can modify record
249        //$this->model->acl->can_change($obj, 'id');
250
251		$set = array();
252		$execute = array();
253		$columns = array();
254		foreach ($obj->get_columns() as $column) {
255            if ($obj->$column === null) continue;
256            //id is special -> when null we insert new row
257//		    if ($column == 'id' && $obj->id == NULL) continue;
258
259//            if ($obj->$column === null) {
260//                throw new \Exception('cannot save object becouse it has uninitialized parameter: '.$column);
261//            }
262			$set[] = ":$column";
263			$columns[] = $column;
264            $value = $obj->$column;
265            if ($value === '') {
266                $execute[':'.$column] = null;
267            } else {
268                $execute[':'.$column] = $value;
269            }
270		}
271
272		$query = 'REPLACE INTO '.$this->get_table_name().'
273							('.implode(',', $columns).')
274							VALUES ('.implode(',', $set).')';
275
276		$sth = $this->model->db->prepare($query);
277		$res = $sth->execute($execute);
278
279        //new object is created
280        if ($obj->id === NULL) {
281            $reflectionClass = new \ReflectionClass($obj);
282            $reflectionProperty = $reflectionClass->getProperty('id');
283            $reflectionProperty->setAccessible(true);
284            $reflectionProperty->setValue($obj, $this->model->db->lastInsertId());
285        }
286//            $id = $this->model->db->lastInsertId();
287//            $obj->set_id($id);
288
289//        }
290
291//		return $id;
292	}
293
294	public function initial_save(Entity $obj, $data) {
295        if ($obj->id != NULL) {
296            throw new \Exception('row already saved. use update_save');
297        }
298    }
299
300    public function update_save(Entity $obj, $data) {
301        if ($obj->id == NULL) {
302            throw new \Exception('row not saved. use initial_save()');
303        }
304    }
305
306	protected function delete_from_db($id) {
307		$q = 'DELETE FROM '.$this->get_table_name().' WHERE id = ?';
308		$sth = $this->model->db->prepare($q);
309		$sth->execute(array($id));
310	}
311
312	public function delete(Entity $obj) {
313        $this->model->acl->can($obj, 'id', BEZ_PERMISSION_DELETE);
314		$this->delete_from_db($obj->id);
315	}
316}
317