FRED™  3.0
FRED™: Framework for Rapid and Easy Development
Db.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use \Rsi\Fred\Controller;
6 
7 class Db extends \Rsi\Fred\Controller\Provider{
8 
9  const COLUMN_TYPE_GLUE = '__';
10 
11  public $select = '*'; //!< Columns to select.
12  public $table = null; //!< Table(s) to select from (including joins and unions).
13  public $search = []; //!< Custom search (key = column, value = SQL - :ref and :count can be used as placeholders for the
14  // searched value(s) and the number of values).
15  public $where = null; //!< Basic where clause (without the 'where' keyword; empty = all rows).
16  public $args = []; //!< Fixed parameters (in contrast to those provided during actions).
17 
18  protected $_db = null;
19 
20  /**
21  * Return a single row from the dataset.
22  * @param string $select Column(s) to select.
23  * @param string $key Column to use to find the record.
24  * @param mixed $value Value to use to find the record.
25  * @param array $params Extra parameters.
26  * @return mixed Row (multi column) or value (single column).
27  */
28  protected function single($select,$key,$value,$params = null){
29  return \Rsi::nothing($value) ? null : $this->db->single($this->db->limit("
30  select $select from {$this->table}
31  where `$key` = :this" . ($this->where ? '
32  and (' . $this->where . ')' : ''),1),
33  array_merge($params ?: [],$this->args,['this' => $value])
34  );
35  }
36 
37  public function trans($column,$key,$value,$params = null){
38  return $column == $key ? $value : $this->single("`$column`",$key,$value,$params);
39  }
40 
41  public function exists($key,$value,$params = null){
42  return (bool)$this->single(1,$key,$value,$params);
43  }
44 
45  protected function implodeMulti($where,$enclose = false){
46  if(count($where) <= 1) return array_pop($where);
47  if($enclose) foreach($where as &$arg) $arg = "($arg)";
48  unset($arg);
49  return '(' . implode(' or ',$where) . ')';
50  }
51  /**
52  * Build a where statement for use in an SQL query.
53  * @param array $search Search criteria.
54  * @param array $args Bind variables for the query.
55  * @param string $strict Where statement (including basic where clause, including 'where' keyword - empty if no criteria).
56  * @param string $loose Where statement with loose evaluation of a 'like' criterion (including basic where clause, including
57  * 'where' keyword - empty if no criteria or not different to basic where clause).
58  * @param string $all Where statement that will find all records (both strict and loose).
59  */
60  protected function where($search,&$args,&$strict,&$loose = null,&$all = null){
61  $strict = $loose = [];
62 
63  foreach($search as $column => $values){
64  $where = [];
65  foreach($values as $type => $ref){
66  $operator = null;
67  if(array_key_exists($column,$this->search)){
68  $args[$key = 's' . count($args)] = $ref;
69  $args[$key_count = 'c' . count($args)] = count($ref);
70  $where[] = strtr($this->search[$column],[':ref' => ':' . $key,':count' => ':' . $key_count]);
71  }
72  else switch($type){
73  case self::SEARCH_FROM:
74  $operator = '>=';
75  case self::SEARCH_TO:
76  if(!$operator) $operator = '<=';
77  case self::SEARCH_EXACT:
78  if($ref === null) $where[] = "`$column` is null";
79  else{
80  if(!$operator) $operator = '=';
81  $args[$key = 'v' . count($args)] = $ref;
82  $where[] = "`$column` " . (is_array($ref) ? "in (:$key)" : "$operator :$key");
83  }
84  break;
85  case self::SEARCH_DATE:
86  $dates = [];
87  foreach((array)$ref as $date) if($date = \Rsi\Fred\DateTime\Date::create($date)){
88  $args[$key_from = 'd' . count($args)] = (string)$date;
89  $args[$key_to = 'd' . count($args)] = (string)$date->add();
90  $dates[] = "`$column` >= :$key_from and `$column` < :$key_to";
91  }
92  $where[] = $this->implodeMulti($dates,true);
93  break;
94  default:
95  $likes = $loose_likes = [];
96  foreach((array)$ref as $like) if($like){
97  if(preg_match('/^\\/.+\\/$/',$like)) try{
98  preg_match($like,''); //test it
99  $args[$key = 'r' . count($args)] = substr($like,1,-1);
100  $likes[] = "`$column` regexp :$key";
101  }
102  catch(\Exception $e){
103  $this->_controller->component('log')->debug("Invalid reg-ex '$like': " . str_replace('preg_match(): Compilation failed: ','',$e->getMessage()),__FILE__,__LINE__);
104  $likes[] = '1 = 0';
105  }
106  elseif(substr($like = strtr($like,['?' => '_','*' => '%']),0,1) == '-'){
107  $args[$key = 'x' . count($args)] = '%' . substr($like,1) . '%';
108  $likes[] = "(`$column` is null or `$column` not like :$key)";
109  }
110  else{
111  $args[$key = 'l' . count($args)] = $like . '%';
112  $likes[] = "`$column` like :$key";
113  if(substr($like,0,1) != '%'){
114  $args[$key = 'l' . count($args)] = '%' . $like . '%';
115  $loose_likes[] = "`$column` like :$key";
116  }
117  }
118  }
119  if($likes){
120  $where[] = $this->implodeMulti($likes);
121  if($loose_likes) $loose[$column] = $this->implodeMulti($loose_likes);
122  }
123  }
124  }
125  if($where) $strict[$column] = implode(' and ',$where);
126  }
127 
128  $where = 'where ' . ($this->where ? '(' . $this->where . ')' . ($strict ? "\nand " : '') : '');
129  $all = $this->where || $strict ? $where . implode(' and ',$loose + $strict) : null;
130  $loose = $loose ? $where . 'not (' . implode(' and ',$strict) . ")\nand " . implode(' and ',array_merge($strict,$loose)) : null;
131  $strict = $this->where || $strict ? $where . implode(' and ',$strict) : null;
132  }
133  /**
134  * Build an order statement for an SQL query.
135  * @param array $order Sort order for the result.
136  * @return string Order statement (including 'order by' keyword - empty if no criteria).
137  */
138  protected function order($order){
139  $columns = [];
140  foreach($order + $this->order as $column => $type){
141  $column = "`$column`";
142  if($type & SORT_DESC) $column .= ' desc';
143  $columns[] = $column;
144  }
145  return $columns ? 'order by ' . implode(',',$columns) : null;
146  }
147 
148  protected function _search($search,$order,$params,$offset,$limit){
149  $args = array_merge($params ?: [],$this->args);
150  $this->where($search,$args,$strict,$loose);
151  $order = $this->order($order ?: []);
152  session_write_close();
153  $select = "select {$this->select}\nfrom {$this->table}";
154  $sql = "$select\n$strict\n$order";
155  if($limit) $sql = $this->db->limit($sql,$limit,$offset);
156  $records = $this->db->all($sql,$args);
157  if($loose && (!$limit || (($count = count($records)) < $limit))){
158  $sql = "$select\n$loose\n$order";
159  if($limit){
160  if($count){
161  $offset = 0;
162  $limit -= $count;
163  }
164  elseif($offset) $offset -= $this->db->single("select count(*) from {$this->table}\n$strict",$args);
165  $sql = $this->db->limit($sql,$limit,$offset);
166  }
167  $records = array_merge($records,$this->db->all($sql,$args));
168  }
169  session_start();
170  return $records;
171  }
172 
173  protected function _totals($search,$totals,$params){
174  if(!$totals) return [];
175  $args = array_merge($params ?: [],$this->args);
176  $this->where($search,$args,$strict,$loose,$all);
177  session_write_close();
178  $columns = ['count(*) as ' . ($count_alias = self::COLUMN_TYPE_GLUE . Controller::TOTAL_COUNT)];
179  foreach($totals as $column => &$types) foreach(($types = (array)$types) as $type){
180  $alias = $column . self::COLUMN_TYPE_GLUE . $type;
181  switch($type){
182  case Controller::TOTAL_COUNT: break;
183  case Controller::TOTAL_UNIQUE: $columns[] = "count(distinct `$column`) as `$alias`"; break;
184  case Controller::TOTAL_NULL: $columns[] = "sum(if(`$column` is null,1,0)) as `$alias`"; break;
185  case Controller::TOTAL_NOT_NULL: $columns[] = "sum(if(`$column` is null,0,1)) as `$alias`"; break;
186  default: $columns[] = "$type(`$column`) as `$alias`";
187  }
188  }
189  unset($types);
190  $select = 'select ' . implode(',',$columns) . "\nfrom {$this->table}\n";
191  $alias_totals = $this->db->single($select . $all,$args,false);
192  session_start();
193  $result = [];
194  foreach($totals as $column => $types){
195  $result[$column] = [];
196  foreach($types as $type) $result[$column][$type] =
197  $alias_totals[$type == Controller::TOTAL_COUNT ? $count_alias : $column . self::COLUMN_TYPE_GLUE . $type];
198  }
199  return $result;
200  }
201 
202  protected function _group($search,$group,$column,$total,$limit,$params){
203  $args = array_merge($params ?: [],$this->args);
204  $this->where($search,$args,$strict,$loose,$all);
205  switch($total){
206  case Controller::TOTAL_UNIQUE: $total = "count(distinct `$column`)"; break;
207  default: $total = "$total(`$column`)";
208  }
209  session_write_close();
210  $sql = "select `$group`,$total from {$this->table}\n$all\ngroup by `$group`\norder by ";
211  $result = $this->db->record($limit ? $this->db->limit($sql . $total . ($limit > 0 ? '' : ' desc'),abs($limit)) : $sql . "`$group`",$args);
212  session_start();
213  return $result;
214  }
215 
216  protected function setDb($value){
217  $this->_db = $value;
218  }
219 
220  protected function getDb(){
221  if($this->_db === null) $this->_db = $this->_controller->component('db');
222  return $this->_db;
223  }
224 
225  protected function getId(){
226  if(!$this->_id) $this->_id = md5($this->select . $this->table . $this->where);
227  return $this->_id;
228  }
229 
230 }
single($select, $key, $value, $params=null)
Return a single row from the dataset.
Definition: Db.php:28
_group($search, $group, $column, $total, $limit, $params)
Definition: Db.php:202
where($search, &$args, &$strict, &$loose=null, &$all=null)
Build a where statement for use in an SQL query.
Definition: Db.php:60
$table
Table(s) to select from (including joins and unions).
Definition: Db.php:12
$where
Basic where clause (without the &#39;where&#39; keyword; empty = all rows).
Definition: Db.php:15
order($order)
Build an order statement for an SQL query.
Definition: Db.php:138
$search
Custom search (key = column, value = SQL - :ref and :count can be used as placeholders for the...
Definition: Db.php:13
_search($search, $order, $params, $offset, $limit)
Definition: Db.php:148
$select
Columns to select.
Definition: Db.php:11
trans($column, $key, $value, $params=null)
Definition: Db.php:37
$args
Fixed parameters (in contrast to those provided during actions).
Definition: Db.php:16
Framework for Rapid and Easy Development.
Definition: Fred.php:18
implodeMulti($where, $enclose=false)
Definition: Db.php:45
$order
Basic/default order (same format as search function).
Definition: Provider.php:16
search($search, $order=null, $params=null, $offset=0, $limit=null)
Search for records in the dataset.
Definition: Provider.php:64
exists($key, $value, $params=null)
Definition: Db.php:41
_totals($search, $totals, $params)
Definition: Db.php:173