00001 <?php
00002
00003 class SQuery extends SObject {
00004 const MODE_SINGLE = 0;
00005 const MODE_TABLE_PREFIX = 1;
00006 const MODE_PREFIX = 2;
00007 const MODE_MULTI = 3;
00008
00009 private $baseClass = null;
00010 private $mode = self::MODE_SINGLE;
00011 private $prefix = 'Base';
00012 private $depth = 1;
00013 private $criteria = array();
00014 private $limit = array();
00015 private $order = array();
00016 private $count = false;
00017 private $groupBy = array();
00018 private $having = array();
00019
00020 private $extraJoins = array();
00021 private $extraAttrs = array();
00022 private $extraTables = array();
00023
00024
00025 private $mapping = null;
00026 private $attributeList = null;
00027 private $query = '';
00028
00029 public function __construct() {
00030 parent::__construct();
00031 }
00032
00041 public function setBaseClass($cl) { $this->baseClass = $cl; }
00042 public function getBaseClass() { return $this->baseClass; }
00043
00056 public function setMode($mode) {
00057 if($mode < 0 || $mode > 3)
00058 $this->setError("Invalid mode specified: " . htmlentities($mode));
00059 else
00060 $this->mode = $mode;
00061 }
00062 public function getMode() { return $this->mode; }
00063
00069 public function setPrefix($prefix) {
00070 if(!is_string($prefix) || $prefix == "")
00071 $this->setError("Invalid prefix specified: " . htmlentities($prefix));
00072 else
00073 $this->prefix = $prefix;
00074 }
00075 public function getPrefix() { return $this->prefix; }
00076
00082 public function setDepth($depth) {
00083 if(!ctype_digit((string)$depth) || $depth < 0 || $depth > 100)
00084 $this->setError("Invalid depth specified: '" . htmlentities($depth) . "'");
00085 else
00086 $this->depth = $depth;
00087 }
00088 public function getDepth() { return $this->depth; }
00089
00097 public function setCriteria($criteria) {
00098 if(!is_array($criteria) && !is_string($criteria) && !is_numeric($criteria))
00099 $this->setError("Invalid criteria specified: " . htmlentities($criteria));
00100 else
00101 $this->criteria = $criteria;
00102 }
00103 public function getCriteria() { return $this->criteria; }
00104
00112 public function setLimit($limit) {
00113 if(!is_array($limit))
00114 $this->setError("Invalid limit specified: " . htmlentities($limit));
00115 else
00116 $this->limit = $limit;
00117 }
00118 public function getLimit() { return $this->limit; }
00119
00127 public function setOrder($order) {
00128 if(!is_array($order) && !is_string($order))
00129 $this->setError("Invalid order specified: " . htmlentities($order));
00130 else
00131 $this->order = $order;
00132 }
00133 public function getOrder() { return $this->order; }
00134
00140 public function setCount($count) {
00141 if($count !== false && $count !== true)
00142 $this->setError("Invalid count specified: " . htmlentities($count));
00143 else
00144 $this->count = $count;
00145 }
00146 public function isCount() { return $this->count; }
00147
00157 public function setGroupBy($groupBy) {
00158 if(!is_array($groupBy))
00159 $this->setError("Invalid group by specified: " . htmlentities($groupBy));
00160 else
00161 $this->groupBy = $groupBy;
00162 }
00163 public function getGroupBy() { return $this->groupBy; }
00164
00173 public function setHaving($having) {
00174 if(!is_array($having) && !is_string($having) && !is_numeric($having))
00175 $this->setError("Invalid having specified: " . htmlentities($having));
00176 else
00177 $this->having = $having;
00178 }
00179 public function getHaving() { return $this->having; }
00180
00189 public function setExtraJoins($extra) {
00190 if(!is_array($extra))
00191 $this->setError("Invalid extra joins specified: " . htmlentities($extra));
00192 else
00193 $this->extraJoins = $extra;
00194 }
00195 public function getExtraJoins() { return $this->extraJoins; }
00196
00206 public function setExtraAttrs($extra) {
00207 if(!is_array($extra))
00208 $this->setError("Invalid extra attrs specified: " . htmlentities($extra));
00209 else
00210 $this->extraAttrs = $extra;
00211 }
00212 public function getExtraAttrs() { return $this->extraAttrs; }
00213
00224 public function setExtraTables($extra) {
00225 if(!is_array($extra))
00226 $this->setError("Invalid extra tables specified: " . htmlentities($extra));
00227 else
00228 $this->extraTables = $extra;
00229 }
00230 public function getExtraTables() { return $this->extraTables; }
00231
00232 public function getMapping() { return $this->mapping; }
00233 public function getAttributeList() { return $this->attributeList; }
00234 public function getQuery() { return $this->query; }
00235
00239 public function buildMapping() {
00240 $class = $this->baseClass;
00241 $mode = $this->mode;
00242 $multiDepth = $this->depth;
00243
00244 $attrArray = array();
00245 $attrMap = array();
00246
00247 if($mode !== self::MODE_MULTI) {
00248 $attrs = call_user_func(array($class, 'getAttributesStatic'));
00249 $table = call_user_func(array($class, 'getTableNameStatic'));
00250 foreach($attrs as $attr => $info) {
00251 if($info['type'] != 'int' && $info['type'] != 'string' && $info['type'] != 'float' && $info['type'] != 'time')
00252 continue;
00253 if(isset($info['commit']) && $info['commit'] == SModel2::COMMIT_NOSOURCE)
00254 continue;
00255 if($mode === self::MODE_SINGLE) {
00256 $attrArray[] = "$table.$attr AS $attr";
00257 $attrMap[$attr] = "$table.$attr";
00258 }
00259 else if($mode === self::MODE_TABLE_PREFIX) {
00260 $attrArray[] = "$table.$attr AS `$table.$attr`";
00261 $attrMap["$table.$attr"] = "$table.$attr";
00262 $attrMap[$attr] = "$table.$attr";
00263 }
00264 else if($mode == self::MODE_PREFIX) {
00265 $attrArray[] = "`$this->prefix`.$attr AS `$this->prefix.$attr`";
00266 $attrMap["$this->prefix.$attr"] = "`$this->prefix`.$attr";
00267 $attrMap[$attr] = "`$this->prefix`.$attr";
00268 }
00269 }
00270
00271 $this->mapping = array(
00272 $this->prefix => array(
00273 $table, 'FROM ' . $table .
00274 ($mode !== self::MODE_SINGLE && $mode !== self::MODE_TABLE_PREFIX ? " AS $this->prefix" : ''),
00275 $attrArray, $attrMap
00276 )
00277 );
00278
00279 $this->attributeList = implode(', ', $attrArray);
00280 }
00281 else {
00282 $childAttrs = call_user_func(array($class, 'getAttributesStatic'));
00283 $childName = call_user_func(array($class, 'getTableNameStatic'));
00284 $childAttrArray = array();
00285 $childAttrMap = array();
00286 foreach($childAttrs as $attr => $attrInfo) {
00287 if(SDatabaseModel::isDatabaseAttributeStatic($attrInfo)) {
00288 $childAttrArray[] = "`$this->prefix`.$attr AS `$this->prefix.$attr`";
00289 $childAttrMap["$this->prefix.$attr"] = "`$this->prefix`.$attr";
00290 $childAttrMap[$attr] = "`$this->prefix`.$attr";
00291 }
00292 }
00293 $this->attributeList = implode(', ', $childAttrArray);
00294 $mapping = array();
00295 $mapping[$this->prefix] = array($childName, 'FROM ' . $childName . ' AS `' . $this->prefix . '`', $childAttrArray, $childAttrMap);
00296
00297 $deps = eval("return $class" . "::\$DEPENDENCIES;");
00298
00299 if($multiDepth > 0)
00300 $this->genAttrsRec($this->prefix, $class, $this->prefix, $deps, $mapping, $depsSeen, $multiDepth);
00301
00302 if(count($this->extraTables) > 0) {
00303 foreach($this->extraTables as $prefix => $class) {
00304 $clAttrs = call_user_func(array($class, 'getAttributesStatic'));
00305 $table = call_user_func(array($class, 'getTableNAmeStatic'));
00306 $attrs = array();
00307 $attrMaps = array();
00308 foreach($clAttrs as $a => $info) {
00309 if(SDatabaseModel::isDatabaseAttributeStatic($info)) {
00310 $attrs[] = "`$prefix`.$a AS `$prefix.$a`";
00311 $attrMaps["$prefix.$a"] = "`$prefix`.$a";
00312 }
00313 }
00314 $mapping[$prefix] = array($table, null, $attrs, $attrMaps);
00315 }
00316 }
00317
00318 ksort($mapping);
00319 $this->mapping = $mapping;
00320 }
00321 }
00322
00323 private function genAttrsRec($prefix, $curTable, $curName, $deps, &$mapping, &$depsSeen, $depth) {
00324
00325 if(!isset($deps[$curTable]))
00326 return;
00327
00328 $toDo = array();
00329
00330 $parAttrs = call_user_func(array($curTable, 'getAttributesStatic'));
00331 foreach($deps[$curTable] as $child => $parentRef) {
00332 $childName = call_user_func(array($child, 'getTableNameStatic'));
00333 $fAttrInfo = $parAttrs[$parentRef];
00334
00335 if($prefix == '')
00336 $newPrefix = $fAttrInfo['foreign']['object'];
00337 else
00338 $newPrefix = $prefix . '.' . $fAttrInfo['foreign']['object'];
00339
00340 if(isset($depsSeen[$curTable . $child]))
00341 continue;
00342
00343 $depsSeen[$curTable . $child] = true;
00344
00345 $mapping[$newPrefix] = array(
00346 $fAttrInfo['foreign']['table'],
00347 "LEFT JOIN {$fAttrInfo['foreign']['table']} AS `$newPrefix` ON `$newPrefix`.id = `$prefix`.$parentRef"
00348 );
00349
00350 $childAttrArray = array();
00351 $childAttrMap = array();
00352 $childAttrs = call_user_func(array($child, 'getAttributesStatic'));
00353
00354 foreach($childAttrs as $attr => $attrInfo) {
00355 if(SDatabaseModel::isDatabaseAttributeStatic($attrInfo)) {
00356 $childAttrArray[] = "`$newPrefix`.$attr AS `$newPrefix.$attr`";
00357 $childAttrMap["$newPrefix.$attr"] = "`$newPrefix`.$attr";
00358 }
00359 }
00360
00361 $this->attributeList += ', ' . implode(', ', $childAttrArray);
00362
00363 $mapping[$newPrefix][] = $childAttrArray;
00364 $mapping[$newPrefix][] = $childAttrMap;
00365
00366 $toDo[] = array($newPrefix, $child, $childName);
00367 }
00368
00369 $depth--;
00370 if($depth > 0) {
00371 foreach($toDo as $item) {
00372 $this->genAttrsRec($item[0], $item[1], $item[2], $deps, $mapping, $depsSeen, $depth);
00373 }
00374 }
00375 }
00376
00377 public function buildQuery() {
00378 STimer::start('buildQuery');
00379
00380 if($this->mapping == null)
00381 $this->buildMapping();
00382
00383 $mapping = $this->mapping;
00384
00385 $attrs = array();
00386 $attrMaps = array();
00387 $joins = '';
00388
00389 foreach($mapping as $prefix => $info) {
00390 if(!$this->count)
00391 $attrs[] = implode(',', $info[2]);
00392 if($info[1] != null)
00393 $joins .= "\n" . $info[1];
00394 if($info[3] !== null)
00395 $attrMaps += $info[3];
00396 }
00397
00398 if(count($this->extraAttrs) > 0) {
00399 foreach($this->extraAttrs as $a => $b) {
00400 if(ctype_digit((string)$a)) {
00401 $attrs[] = "`$b`";
00402 $attrMaps["$b"] = $b;
00403 }
00404 else {
00405 $attrs[] = "$a AS `$b`";
00406 $attrMaps["$b"] = "`$b`";
00407 }
00408 }
00409 }
00410
00411 if($this->count)
00412 $query = 'SELECT COUNT(*) ' . $joins;
00413 else
00414 $query = 'SELECT ' . implode(",\n", $attrs) . $joins;
00415
00416 if(count($this->extraJoins) > 0)
00417 $query .= ' ' . implode(' ', $this->extraJoins);
00418
00419 if(count($this->criteria) > 0) {
00420 $criteriaStr = DBI2::criteriaToString($this->criteria, count($attrMaps) > 0 ? $attrMaps : null);
00421 if($criteriaStr === false)
00422 return false;
00423 if($criteriaStr != '')
00424 $query .= " WHERE $criteriaStr";
00425 }
00426
00427 if(count($this->groupBy) > 0)
00428 $query .= ' GROUP BY ' . implode(',', $this->groupBy);
00429
00430 if(count($this->having) > 0) {
00431 $havingStr = DBI2::criteriaToString($this->having, count($attrMaps) > 0 ? $attrMaps : null);
00432 if($havingStr === false)
00433 return false;
00434 if($havingStr != '')
00435 $query .= ' HAVING ' . $havingStr;
00436 }
00437
00438 if(count($this->order) > 0) {
00439 $orderStr = DBI2::orderToString($this->order, count($attrMaps) > 0 ? $attrMaps : null);
00440 if($orderStr === false)
00441 return false;
00442 if($orderStr != '')
00443 $query .= $orderStr;
00444 }
00445
00446 if(count($this->limit) > 0) {
00447 $limitStr = DBI2::limitToString($this->limit);
00448 if($limitStr === false)
00449 return false;
00450 if($limitStr != '')
00451 $query .= $limitStr;
00452 }
00453
00454 if($this->count && count($this->groupBy) > 0)
00455 $query = "SELECT COUNT(*) FROM ($query) AS t";
00456
00457
00458
00459 STimer::end('buildQuery');
00460
00461 $this->query = $query;
00462 return $query;
00463 }
00464 }
00465
00466 ?>