FRED™  3.0
FRED™: Framework for Rapid and Easy Development
Record.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use \Rsi\Fred\Controller;
6 
7 class Record extends \Rsi\Fred\Controller\Provider{
8 
9  public $data = null;
10  public $search = []; //!< Custom search (key = column, value = callback function - params: the value for the column in the
11  // record, the searched value(s), and the complete record; return true on match).
12 
13  protected $_cache = [];
14 
15  protected function data($params){
16  if(is_callable($data = $this->data)){
17  if(!array_key_exists($key = 'data-' . md5(serialize($params)),$this->_cache)) $this->_cache[$key] = call_user_func($data,$params,$this->id);
18  $data = $this->_cache[$key];
19  }
20  return $data;
21  }
22 
23  public function trans($column,$key,$value,$params = null){
24  if(\Rsi::nothing($value)) return null;
25  foreach($this->data($params) as $record) if(array_key_exists($key,$record) && ($record[$key] == $value)) return $record[$column] ?? null;
26  return false;
27  }
28 
29  public function exists($key,$value,$params = null){
30  foreach($this->data($params) as $record) if(array_key_exists($key,$record) && ($record[$key] == $value)) return true;
31  return false;
32  }
33 
34  protected function filter($search,$params,$loose = false){
35  $this->_controller->component('log')->debug(__CLASS__ . "::filter(search,params,loose)",__FILE__,__LINE__,['id' => $this->_id] + compact('search','params','loose'));
36  $records = [];
37  foreach($this->data($params) as $record){
38  $match = true;
39  foreach($search as $column => $values){
40  $value = \Rsi\Record::iget($record,$column);
41  foreach($values as $type => $ref){
42  if(array_key_exists($column,$this->search)) $match = call_user_func($this->search[$column],$value,$ref,$record);
43  else switch($type){
44  case self::SEARCH_FROM:
45  $match = $value >= $ref;
46  break;
47  case self::SEARCH_TO:
48  $match = $value <= $ref;
49  break;
50  case self::SEARCH_EXACT:
51  foreach((array)$ref as $ref)
52  if(($ref && !($match = !strcasecmp($value,$ref))) || (($ref === null) && !($match = ($value === null)))) break;
53  break;
54  case self::SEARCH_DATE:
55  foreach((array)$ref as $date)
56  if(($date = \Rsi\Fred\DateTime\Date::create($date)) && !($match = ($value >= (string)$date) && ($value < (string)$date->add()))) break;
57  break;
58  case self::SEARCH_LIKE:
59  foreach((array)$ref as $like) if($like){
60  if(!preg_match('/^\\/.+\\/$/',$like)){
61  if($not = substr($like,0,1) == '-') $like = substr($like,1);
62  $mask = strtr(preg_quote($like,'/'),['\\?' => '.','\\*' => '.*']);
63  if($loose) $mask = "(?!$mask).+$mask";
64  $match = ($not xor preg_match("/^$mask/i",$value));
65  }
66  else try{
67  $match = preg_match($like . 'i',$value);
68  }
69  catch(\Exception $e){
70  $this->_controller->component('log')->debug("Invalid reg-ex '$like': " . str_replace('preg_match(): Compilation failed: ','',$e->getMessage()),__FILE__,__LINE__);
71  return [];
72  }
73  if(!$match) break;
74  }
75  break;
76  }
77  if(!$match) break;
78  }
79  if(!$match) continue 2;
80  }
81  $records[] = $record;
82  }
83  return $records;
84  }
85 
86  protected function sort($records,$order){
87  if(($order !== false) && ($order || $this->order)){
88  if($order){
89  $default = $this->order;
90  foreach($order as $column => &$type) if(array_key_exists($column,$default)){
91  $type |= ($default[$column] & 0xf0);
92  unset($default[$column]);
93  }
94  unset($type);
95  $order += $default;
96  }
97  else $order = $this->order;
98  $params = [];
99  foreach($order as $column => $type) $params = array_merge($params,[\Rsi\Record::column($records,$column),$type & 0xf,$type >> 4]);
100  $params[] = &$records;
101  call_user_func_array('array_multisort',$params);
102  }
103  return $records;
104  }
105 
106  protected function _search($search,$order,$params,$offset,$limit){
107  if($search){
108  if(!array_key_exists($key = 'search-' . md5(serialize(compact('search','params'))),$this->_cache))
109  $this->_cache[$key] = $this->filter($search,$params);
110  $records = $this->sort($this->_cache[$key],$order);
111  foreach($search as $values) foreach($values as $type => $ref) if($type == self::SEARCH_LIKE){
112  if(!array_key_exists($key .= '-loose',$this->_cache)) $this->_cache[$key] = $this->filter($search,$params,true);
113  $records = array_merge($records,$this->sort($this->_cache[$key],$order));
114  break 2;
115  }
116  }
117  else $records = $this->sort($this->data($params),$order);
118  return $limit ? array_slice($records,$offset,$limit) : $records;
119  }
120 
121  protected function _totals($search,$totals,$params){
122  $results = $counts = [];
123 
124  foreach($totals as $column => &$types){
125  $results[$column] = $counts[$column] = [];
126  foreach(($types = is_array($types) ? $types : [$types]) as $type)
127  $results[$column][$type] = $counts[$column][$type] = null;
128  }
129  unset($types);
130 
131  foreach($this->_search($search,false,$params,null,null) as $record){
132  foreach($totals as $column => $types) foreach($types as $type){
133  $result = \Rsi\Record::iget($record,$column);
134  switch($type){
136  $results[$column][$type]++;
137  break;
139  $counts[$column][$type]++;
141  $results[$column][$type] += $result;
142  break;
144  if(($result !== null) && (($results[$column][$type] === null) || ($result > $results[$column][$type])))
145  $results[$column][$type] = $result;
146  break;
148  if(($result !== null) && (($results[$column][$type] === null) || ($result < $results[$column][$type])))
149  $results[$column][$type] = $result;
150  break;
152  if(!$results[$column][$type]) $results[$column][$type] = [];
153  if(!in_array($result,$results[$column][$type])) $results[$column][$type][] = $result;
154  break;
156  if($result === null) $results[$column][$type]++;
157  break;
159  if($result !== null) $results[$column][$type]++;
160  break;
161  }
162  }
163  }
164 
165  foreach($totals as $column => $types) foreach($types as $type){
166  if(is_array($results[$column][$type])) $results[$column][$type] = count($results[$column][$type]);
167  if($counts[$column][$type]) $results[$column][$type] /= $counts[$column][$type];
168  }
169 
170  return $results;
171  }
172 
173  protected function _group($search,$group,$column,$total,$limit,$params){
174  $result = [];
175  foreach(array_unique(\Rsi\Record::column($this->_search($search,false,$params,null,null),$group)) as $value)
176  $result[$value] = \Rsi\Record::get($this->_totals([$group => [self::SEARCH_EXACT => $value]] + $search,[$column => [$total]],$params),[$column,$total]);
177  if(!$limit) return \Rsi\Record::sort($result,SORT_FLAG_CASE,true);
178  $result = \Rsi\Record::sort($result,SORT_FLAG_CASE);
179  return $limit > 0 ? array_slice($result,0,$limit) : array_reverse(array_slice($result,$limit));
180  }
181 
182 }
_totals($search, $totals, $params)
Definition: Record.php:121
trans($column, $key, $value, $params=null)
Definition: Record.php:23
$search
Custom search (key = column, value = callback function - params: the value for the column in the...
Definition: Record.php:10
_search($search, $order, $params, $offset, $limit)
Definition: Record.php:106
Framework for Rapid and Easy Development.
Definition: Fred.php:18
$order
Basic/default order (same format as search function).
Definition: Provider.php:16
filter($search, $params, $loose=false)
Definition: Record.php:34
search($search, $order=null, $params=null, $offset=0, $limit=null)
Search for records in the dataset.
Definition: Provider.php:64
_group($search, $group, $column, $total, $limit, $params)
Definition: Record.php:173
exists($key, $value, $params=null)
Definition: Record.php:29