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) || ($ref == 'null')) $where[] = "`$column` is null";
79  elseif($ref == '""') $where[] = "(`$column` is null or `$column` = '')";
80  else{
81  if(!$operator) $operator = '=';
82  $args[$key = 'v' . count($args)] = $ref;
83  $where[] = "`$column` " . (is_array($ref) ? "in (:$key)" : "$operator :$key");
84  }
85  break;
86  case self::SEARCH_DATE:
87  $dates = [];
88  foreach((array)$ref as $date) if($date = \Rsi\Fred\DateTime\Date::create($date)){
89  $args[$key_from = 'd' . count($args)] = (string)$date;
90  $args[$key_to = 'd' . count($args)] = (string)$date->add();
91  $dates[] = "`$column` >= :$key_from and `$column` < :$key_to";
92  }
93  $where[] = $this->implodeMulti($dates,true);
94  break;
95  default:
96  $likes = $loose_likes = [];
97  foreach((array)$ref as $like) if($like){
98  if(preg_match('/^\\/.+\\/$/',$like)) try{
99  preg_match($like,''); //test it
100  $args[$key = 'r' . count($args)] = substr($like,1,-1);
101  $likes[] = "`$column` regexp :$key";
102  }
103  catch(\Exception $e){
104  $this->_controller->component('log')->debug("Invalid reg-ex '$like': " . str_replace('preg_match(): Compilation failed: ','',$e->getMessage()),__FILE__,__LINE__);
105  $likes[] = '1 = 0';
106  }
107  elseif(substr($like = strtr($like,['?' => '_','*' => '%']),0,1) == '-'){
108  $args[$key = 'x' . count($args)] = '%' . substr($like,1) . '%';
109  $likes[] = "(`$column` is null or `$column` not like :$key)";
110  }
111  elseif($like == 'null') $likes[] = "`$column` is null";
112  elseif($like == '""') $likes[] = "(`$column` is null or `$column` = '')";
113  else{
114  $args[$key = 'l' . count($args)] = $like . '%';
115  $likes[] = "`$column` like :$key";
116  if(substr($like,0,1) != '%'){
117  $args[$key = 'l' . count($args)] = '%' . $like . '%';
118  $loose_likes[] = "`$column` like :$key";
119  }
120  }
121  }
122  if($likes){
123  $where[] = $this->implodeMulti($likes);
124  if($loose_likes) $loose[$column] = $this->implodeMulti($loose_likes);
125  }
126  }
127  }
128  if($where) $strict[$column] = implode(' and ',$where);
129  }
130 
131  $where = 'where ' . ($this->where ? '(' . $this->where . ')' . ($strict ? "\nand " : '') : '');
132  $all = $this->where || $strict ? $where . implode(' and ',$loose + $strict) : null;
133  $loose = $loose ? $where . 'not (' . implode(' and ',$strict) . ")\nand " . implode(' and ',array_merge($strict,$loose)) : null;
134  $strict = $this->where || $strict ? $where . implode(' and ',$strict) : null;
135  }
136  /**
137  * Build an order statement for an SQL query.
138  * @param array $order Sort order for the result.
139  * @return string Order statement (including 'order by' keyword - empty if no criteria).
140  */
141  protected function order($order){
142  $columns = [];
143  foreach($order + $this->order as $column => $type){
144  $column = "`$column`";
145  if($type & SORT_DESC) $column .= ' desc';
146  $columns[] = $column;
147  }
148  return $columns ? 'order by ' . implode(',',$columns) : null;
149  }
150 
151  protected function _search($search,$order,$params,$offset,$limit){
152  $args = array_merge($params ?: [],$this->args);
153  $this->where($search,$args,$strict,$loose);
154  $order = $this->order($order ?: []);
155  session_write_close();
156  $select = "select {$this->select}\nfrom {$this->table}";
157  $sql = "$select\n$strict\n$order";
158  if($limit) $sql = $this->db->limit($sql,$limit,$offset);
159  $records = $this->db->all($sql,$args);
160  if($loose && (!$limit || (($count = count($records)) < $limit))){
161  $sql = "$select\n$loose\n$order";
162  if($limit){
163  if($count){
164  $offset = 0;
165  $limit -= $count;
166  }
167  elseif($offset) $offset -= $this->db->single("select count(*) from {$this->table}\n$strict",$args);
168  $sql = $this->db->limit($sql,$limit,$offset);
169  }
170  $records = array_merge($records,$this->db->all($sql,$args));
171  }
172  session_start();
173  return $records;
174  }
175 
176  protected function _totals($search,$totals,$params){
177  if(!$totals) return [];
178  $args = array_merge($params ?: [],$this->args);
179  $this->where($search,$args,$strict,$loose,$all);
180  session_write_close();
181  $columns = ['count(*) as ' . ($count_alias = self::COLUMN_TYPE_GLUE . Controller::TOTAL_COUNT)];
182  foreach($totals as $column => &$types) foreach(($types = (array)$types) as $type){
183  $alias = $column . self::COLUMN_TYPE_GLUE . $type;
184  switch($type){
185  case Controller::TOTAL_COUNT: break;
186  case Controller::TOTAL_UNIQUE: $columns[] = "count(distinct `$column`) as `$alias`"; break;
187  case Controller::TOTAL_NULL: $columns[] = "sum(if(`$column` is null,1,0)) as `$alias`"; break;
188  case Controller::TOTAL_NOT_NULL: $columns[] = "sum(if(`$column` is null,0,1)) as `$alias`"; break;
189  default: $columns[] = "$type(`$column`) as `$alias`";
190  }
191  }
192  unset($types);
193  $select = 'select ' . implode(',',$columns) . "\nfrom {$this->table}\n";
194  $alias_totals = $this->db->single($select . $all,$args,false);
195  session_start();
196  $result = [];
197  foreach($totals as $column => $types){
198  $result[$column] = [];
199  foreach($types as $type) $result[$column][$type] =
200  $alias_totals[$type == Controller::TOTAL_COUNT ? $count_alias : $column . self::COLUMN_TYPE_GLUE . $type];
201  }
202  return $result;
203  }
204 
205  protected function _group($search,$group,$column,$total,$limit,$params){
206  $args = array_merge($params ?: [],$this->args);
207  $this->where($search,$args,$strict,$loose,$all);
208  switch($total){
209  case Controller::TOTAL_UNIQUE: $total = "count(distinct `$column`)"; break;
210  default: $total = "$total(`$column`)";
211  }
212  session_write_close();
213  $sql = "select `$group`,$total from {$this->table}\n$all\ngroup by `$group`\norder by ";
214  $result = $this->db->record($limit ? $this->db->limit($sql . $total . ($limit > 0 ? '' : ' desc'),abs($limit)) : $sql . "`$group`",$args);
215  session_start();
216  return $result;
217  }
218 
219  protected function setDb($value){
220  $this->_db = $value;
221  }
222 
223  protected function getDb(){
224  if($this->_db === null) $this->_db = $this->_controller->component('db');
225  return $this->_db;
226  }
227 
228  protected function getId(){
229  if(!$this->_id) $this->_id = md5($this->select . $this->table . $this->where);
230  return $this->_id;
231  }
232 
233 }
Rsi\Fred\Controller\Provider\Db\setDb
setDb($value)
Definition: Db.php:219
Rsi\Fred\Controller\Provider\search
search($search, $order=null, $params=null, $offset=0, $limit=null)
Search for records in the dataset.
Definition: Provider.php:64
Rsi\Fred\Controller\Provider\SEARCH_EXACT
const SEARCH_EXACT
Search for values identical to the reference value (this may also be an array with.
Definition: Provider.php:9
Rsi\Fred\Controller\Provider\Db\$search
$search
Custom search (key = column, value = SQL - :ref and :count can be used as placeholders for the.
Definition: Db.php:13
Rsi\Fred\Controller\Provider\Db\order
order($order)
Build an order statement for an SQL query.
Definition: Db.php:141
Rsi
Rsi\Fred\Controller\Provider\Db\_totals
_totals($search, $totals, $params)
Definition: Db.php:176
Rsi\Fred\Controller\Provider\Db
Definition: Db.php:7
Rsi\Fred\Controller\TOTAL_NULL
const TOTAL_NULL
Definition: Controller.php:14
Rsi\Fred\Controller\Provider\SEARCH_TO
const SEARCH_TO
Search for values smaller than the reference value.
Definition: Provider.php:8
Rsi\Fred\Controller\Provider\Db\where
where($search, &$args, &$strict, &$loose=null, &$all=null)
Build a where statement for use in an SQL query.
Definition: Db.php:60
Rsi\Fred\Controller\Provider\Db\_group
_group($search, $group, $column, $total, $limit, $params)
Definition: Db.php:205
Rsi\Fred\Controller\Provider\Db\implodeMulti
implodeMulti($where, $enclose=false)
Definition: Db.php:45
Rsi\Fred\Controller\Provider\Db\_search
_search($search, $order, $params, $offset, $limit)
Definition: Db.php:151
Rsi\Fred\Controller\Provider\Db\$_db
$_db
Definition: Db.php:18
Rsi\Fred\Controller\Provider\Db\trans
trans($column, $key, $value, $params=null)
Translates a value using the dataset.
Definition: Db.php:37
Rsi\Fred\Controller\Provider\SEARCH_FROM
const SEARCH_FROM
Search for values larger than the reference value.
Definition: Provider.php:7
Rsi\Fred\Controller\Provider\Db\single
single($select, $key, $value, $params=null)
Return a single row from the dataset.
Definition: Db.php:28
Rsi\Fred\Controller\Provider\SEARCH_DATE
const SEARCH_DATE
Search for a single date (24 hours).
Definition: Provider.php:11
Rsi\Fred\Controller\Provider\Db\$where
$where
Basic where clause (without the 'where' keyword; empty = all rows).
Definition: Db.php:15
Rsi\Fred\Controller\TOTAL_UNIQUE
const TOTAL_UNIQUE
Definition: Controller.php:13
Rsi\Fred\Controller\Provider\Db\COLUMN_TYPE_GLUE
const COLUMN_TYPE_GLUE
Definition: Db.php:9
Rsi\Fred\Controller\TOTAL_COUNT
const TOTAL_COUNT
Definition: Controller.php:7
Rsi\Fred\Controller\Provider\Db\$select
$select
Columns to select.
Definition: Db.php:11
Rsi\Fred\Controller\Provider\Db\$table
$table
Table(s) to select from (including joins and unions).
Definition: Db.php:12
Rsi\Fred\Controller\Provider\Db\$args
$args
Fixed parameters (in contrast to those provided during actions).
Definition: Db.php:16
Rsi\Fred\Controller\Provider\$_id
$_id
Definition: Provider.php:21
Rsi\Fred\Controller\Provider\Db\getId
getId()
Definition: Db.php:228
Rsi\Fred\Controller\Provider\Db\exists
exists($key, $value, $params=null)
Check if a record exists in the dataset.
Definition: Db.php:41
Rsi\Fred\Controller\Provider\Db\getDb
getDb()
Definition: Db.php:223
Rsi\Fred\Exception
Definition: Exception.php:5
Rsi\Fred\Controller\Provider\$order
$order
Basic/default order (same format as search function).
Definition: Provider.php:16
Rsi\Fred\Controller\TOTAL_NOT_NULL
const TOTAL_NOT_NULL
Definition: Controller.php:15
Rsi\Fred\Controller\Provider
Definition: Db.php:3