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((is_array($ref) ? $ref : [$ref]) as $ref)
52  if(($ref && !($match = !strcasecmp($value,$ref))) || ((($ref === null) || ($ref == 'null')) && !($match = ($value === null)))) break;
53  elseif(($ref == '""') && !($match = !strcmp($value,''))) break;
54  break;
55  case self::SEARCH_DATE:
56  foreach((array)$ref as $date)
57  if(($date = \Rsi\Fred\DateTime\Date::create($date)) && !($match = ($value >= (string)$date) && ($value < (string)$date->add()))) break;
58  break;
59  case self::SEARCH_LIKE:
60  foreach((array)$ref as $like) if($like){
61  if(!preg_match('/^\\/.+\\/$/',$like)){
62  if($like == 'null') $match = $value === null;
63  elseif($like == '""') $match = !strcmp($value,'');
64  else{
65  if($not = substr($like,0,1) == '-') $like = substr($like,1);
66  $mask = strtr(preg_quote($like,'/'),['\\?' => '.','\\*' => '.*']);
67  if($loose) $mask = "(?!$mask).+$mask";
68  $match = ($not xor preg_match("/^$mask/i",$value));
69  }
70  }
71  else try{
72  $match = preg_match($like . 'i',$value);
73  }
74  catch(\Exception $e){
75  $this->_controller->component('log')->debug("Invalid reg-ex '$like': " . str_replace('preg_match(): Compilation failed: ','',$e->getMessage()),__FILE__,__LINE__);
76  return [];
77  }
78  if(!$match) break;
79  }
80  break;
81  }
82  if(!$match) break;
83  }
84  if(!$match) continue 2;
85  }
86  $records[] = $record;
87  }
88  return $records;
89  }
90 
91  protected function sort($records,$order){
92  if(($order !== false) && ($order || $this->order)){
93  if($order){
94  $default = $this->order;
95  foreach($order as $column => &$type) if(array_key_exists($column,$default)){
96  $type |= ($default[$column] & 0xf0);
97  unset($default[$column]);
98  }
99  unset($type);
100  $order += $default;
101  }
102  else $order = $this->order;
103  $params = [];
104  foreach($order as $column => $type) $params = array_merge($params,[\Rsi\Record::column($records,$column),$type & 0xf,$type >> 4]);
105  $params[] = &$records;
106  call_user_func_array('array_multisort',$params);
107  }
108  return $records;
109  }
110 
111  protected function _search($search,$order,$params,$offset,$limit){
112  if($search){
113  if(!array_key_exists($key = 'search-' . md5(serialize(compact('search','params'))),$this->_cache))
114  $this->_cache[$key] = $this->filter($search,$params);
115  $records = $this->sort($this->_cache[$key],$order);
116  foreach($search as $values) foreach($values as $type => $ref) if($type == self::SEARCH_LIKE){
117  if(!array_key_exists($key .= '-loose',$this->_cache)) $this->_cache[$key] = $this->filter($search,$params,true);
118  $records = array_merge($records,$this->sort($this->_cache[$key],$order));
119  break 2;
120  }
121  }
122  else $records = $this->sort($this->data($params),$order);
123  return $limit ? array_slice($records,$offset,$limit) : $records;
124  }
125 
126  protected function _totals($search,$totals,$params){
127  $results = $counts = $averages = [];
128 
129  foreach($totals as $column => &$types){
130  $results[$column] = $counts[$column] = [];
131  foreach(($types = is_array($types) ? $types : [$types]) as $type)
132  $results[$column][$type] = $counts[$column][$type] = null;
133  }
134  unset($types);
135 
136  foreach(($records = $this->_search($search,false,$params,null,null)) as $record){
137  foreach($totals as $column => $types) foreach($types as $type){
138  $result = \Rsi\Record::iget($record,$column);
139  switch($type){
141  $results[$column][$type]++;
142  break;
144  if(!array_key_exists($column,$averages)) $averages[$column] = \Rsi\Record::average(array_column($records,$column));
145  $result = pow($result - $averages[$column],2);
147  $counts[$column][$type]++;
149  $results[$column][$type] += $result;
150  break;
152  if(($result !== null) && (($results[$column][$type] === null) || ($result > $results[$column][$type])))
153  $results[$column][$type] = $result;
154  break;
156  if(($result !== null) && (($results[$column][$type] === null) || ($result < $results[$column][$type])))
157  $results[$column][$type] = $result;
158  break;
160  if(!$results[$column][$type]) $results[$column][$type] = [];
161  if(!in_array($result,$results[$column][$type])) $results[$column][$type][] = $result;
162  break;
164  if($result === null) $results[$column][$type]++;
165  break;
167  if($result !== null) $results[$column][$type]++;
168  break;
169  }
170  }
171  }
172 
173  foreach($totals as $column => $types) foreach($types as $type){
174  if(is_array($results[$column][$type])) $results[$column][$type] = count($results[$column][$type]);
175  if($counts[$column][$type]) $results[$column][$type] /= $counts[$column][$type];
176  if($type == Controller::TOTAL_STD) $results[$column][$type] = sqrt($results[$column][$type]);
177  }
178 
179  return $results;
180  }
181 
182  protected function _group($search,$group,$column,$total,$limit,$params){
183  $result = [];
184  foreach(array_unique(\Rsi\Record::column($this->_search($search,false,$params,null,null),$group)) as $value)
185  $result[$value] = \Rsi\Record::get($this->_totals([$group => [self::SEARCH_EXACT => $value]] + $search,[$column => [$total]],$params),[$column,$total]);
186  if(!$limit) return \Rsi\Record::sort($result,SORT_FLAG_CASE,true);
187  $result = \Rsi\Record::sort($result,SORT_FLAG_CASE);
188  return $limit > 0 ? array_slice($result,0,$limit) : array_reverse(array_slice($result,$limit));
189  }
190 
191 }
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\Record\$data
$data
Definition: Record.php:9
Rsi
Rsi\Fred\Controller\TOTAL_MIN
const TOTAL_MIN
Definition: Controller.php:12
Rsi\Fred\Controller\Provider\Record\$search
$search
Custom search (key = column, value = callback function - params: the value for the column in the.
Definition: Record.php:10
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\TOTAL_MAX
const TOTAL_MAX
Definition: Controller.php:11
Rsi\Fred\Controller\Provider\Record\exists
exists($key, $value, $params=null)
Check if a record exists in the dataset.
Definition: Record.php:29
Rsi\Fred\Controller\TOTAL_AVG
const TOTAL_AVG
Definition: Controller.php:9
Rsi\Fred\Controller\Provider\SEARCH_LIKE
const SEARCH_LIKE
Search for values that match reference value with wildcard (? = 1 char, * = multiple chars).
Definition: Provider.php:12
Rsi\Fred\Controller\TOTAL_SUM
const TOTAL_SUM
Definition: Controller.php:8
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\SEARCH_DATE
const SEARCH_DATE
Search for a single date (24 hours).
Definition: Provider.php:11
Rsi\Fred\Controller\TOTAL_UNIQUE
const TOTAL_UNIQUE
Definition: Controller.php:13
Rsi\Fred\Controller\Provider\Record\filter
filter($search, $params, $loose=false)
Definition: Record.php:34
Rsi\Fred\Controller\Provider\Record\sort
sort($records, $order)
Definition: Record.php:91
Rsi\Fred\Controller\TOTAL_COUNT
const TOTAL_COUNT
Definition: Controller.php:7
Rsi\Fred\Controller\Provider\Record\trans
trans($column, $key, $value, $params=null)
Translates a value using the dataset.
Definition: Record.php:23
Rsi\Fred\Controller\Provider\Record
Definition: Record.php:7
Rsi\Fred\Controller\Provider\Record\data
data($params)
Definition: Record.php:15
Rsi\Fred\Controller\Provider\Record\_search
_search($search, $order, $params, $offset, $limit)
Definition: Record.php:111
Rsi\Fred\Controller\Provider\Record\$_cache
$_cache
Definition: Record.php:13
Rsi\Fred\Controller\Provider\Record\_totals
_totals($search, $totals, $params)
Definition: Record.php:126
Rsi\Fred\Controller\TOTAL_STD
const TOTAL_STD
Definition: Controller.php:10
Rsi\Fred\Controller\Provider\Record\_group
_group($search, $group, $column, $total, $limit, $params)
Definition: Record.php:182
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