xref: /plugin/bez/mdl/Factory.php (revision e8827d732aaeeee6f7b703c5654f86ca97056383)
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