Framework  3.9
join.inc
Go to the documentation of this file.
1 <?php
5 /**************************************************************
6 
7  Copyright (c) 2007-2010 Sonjara, Inc
8 
9  Permission is hereby granted, free of charge, to any person
10  obtaining a copy of this software and associated documentation
11  files (the "Software"), to deal in the Software without
12  restriction, including without limitation the rights to use,
13  copy, modify, merge, publish, distribute, sublicense, and/or sell
14  copies of the Software, and to permit persons to whom the
15  Software is furnished to do so, subject to the following
16  conditions:
17 
18  The above copyright notice and this permission notice shall be
19  included in all copies or substantial portions of the Software.
20 
21  Except as contained in this notice, the name(s) of the above
22  copyright holders shall not be used in advertising or otherwise
23  to promote the sale, use or other dealings in this Software
24  without prior written authorization.
25 
26  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
28  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33  OTHER DEALINGS IN THE SOFTWARE.
34 
35 *****************************************************************/
36 
37 require_once realpath(dirname(__FILE__)."/data_item.inc");
38 
46 {
47  var $_xmlTagName = "join";
48  var $_classes;
49 
50  function JoinResult($tag = "join")
51  {
52  $this->_xmlTagName = $tag;
53  $this->_classes = array();
54  }
55 
61  function toXML($indent = 0)
62  {
63  $xml = str_repeat(" ", $indent) . "<" . $this->_xmlTagName . ">\n";
64  foreach ($this as $field => $val)
65  {
66  if (is_object($val))
67  {
68  $xml .= $val->toXML($indent + 1);
69  }
70  }
71 
72  $xml .= str_repeat(" ", $indent) . "</" . $this->_xmlTagName . ">\n";
73 
74  return $xml;
75  }
76 
85  function format($template)
86  {
87  $matches = array();
88 
89  if (count($this->_classes) == 1 && preg_match("/^[\\w_]+/"))
90  {
91  $cl = $this->_classes[0];
92  return $this->$cl->$format;
93  }
94 
95  preg_match_all("/\\{([\\w_]+)\.([^}]+)}/", $template, $matches, PREG_SET_ORDER);
96 
97  foreach($matches as $match)
98  {
99  $class = $match[1];
100  $subtemplate = $match[2];
101 
102  $value = is_object($this->$class) ? $this->$class->format("{".$subtemplate."}") : "";
103 
104  $template = str_replace($match[0], $value, $template);
105  }
106 
107  $matches = array();
108 
109  preg_match_all("/\\{([\\w\\d_]+)::([^}]*)}/", $template, $matches, PREG_SET_ORDER);
110 
111  foreach($matches as $match)
112  {
113  $sub = call_user_func(array($match[1], $match[2]), $this);
114  $template = str_replace($match[0], $sub, $template);
115  }
116 
117  return $template;
118  }
119 }
120 
121 class JoinResultIterator implements Iterator, Countable
122 {
123  var $join;
125  var $query;
126  var $result;
127  var $current = null;
128  var $position = -1;
129  var $item;
130 
131  public function __construct($join, $constraints)
132  {
133  $this->join = $join;
134  $this->constraints = $constraints;
135  $this->item = new JoinResult();
136  for($i = 0; $i < count($join->classes); ++$i)
137  {
138  $class = $join->classes[$i];
139  $this->item->$class = new $class;
140  }
141 
142  $this->query = $join->generateQuery($constraints);
143  }
144 
145  public function count()
146  {
147  $this->rewind();
148  trace("Number of Rows: {$this->rowCount}", 4);
149 
150  return $this->rowCount;
151  }
152 
153  public function rewind()
154  {
155  if ($this->position == 0) return;
156 
157  $this->current = null;
158  $this->position = 0;
159 
160  if (!$this->query)
161  {
162  $this->query = $this->join->generateQuery($this->constraints);
163  }
164 
165  try
166  {
168  $this->result = $db->prepare($this->query);
169  $this->result->execute();
170  $this->rowCount = $this->result->rowCount();
171  $this->current = $this->result->fetch();
172  }
173  catch(PDOException $e)
174  {
175  throw new FakoliException($e->getMessage());
176  }
177  }
178 
179  public function current()
180  {
181  for($i = 0; $i < count($this->join->classes); ++$i)
182  {
183  $class = $this->join->classes[$i];
184  $this->item->$class->populate($this->current, $this->join->aliases[$this->item->$class->table]);
185  }
186 
187  foreach($this->join->pseudos as $class => $lookup)
188  {
189  foreach($this->join->aliases as $table => $prefix)
190  {
191  $keyAlias = "{$prefix}.{$lookup['index']}";
192 
193  $key = $this->current[$keyAlias];
194 
195  if (array_key_exists($key, $lookup['items']))
196  {
197  $this->item->$class = $lookup['items'][$key];
198  break;
199  }
200  }
201  }
202 
203  return $this->item;
204  }
205 
206  public function valid()
207  {
208  return ($this->current != null);
209  }
210 
211  public function next()
212  {
213  $this->current = $this->result->fetch();
214  ++$this->position;
215  }
216 
217  public function key()
218  {
219  return $this->position;
220  }
221 
222 }
223 
232 abstract class AbstractJoin
233 {
234  var $classes = array();
235  var $protos = array();
236  var $xrefs = array();
237 
238  var $xrefFieldMaps = array();
239 
240  var $aliases = array();
241  var $xrefAliases = array();
242  var $constraints = array();
243 
244  var $unique = false;
245  var $tag = "join";
246 
247  var $excludedKeys = array();
248  var $pseudos = array();
249 
256  function add($class)
257  {
258  $this->classes[] = $class;
259 
260  $this->protos[$class] = new $class;
261 
262  if (func_num_args() > 1)
263  {
264  $filter = new InclusionFilter();
265 
266  if (func_get_arg(1) !== false)
267  {
268  for($i = 1; $i < func_num_args(); ++$i)
269  {
270  $filter->add(func_get_arg($i));
271  }
272  }
273 
274  $this->protos[$class]->filter = $filter;
275  }
276 
277  return $this;
278  }
279 
285  function setFilter($class, $filter)
286  {
287  $this->protos[$class]->filter = $filter;
288  return $this;
289  }
290 
296  function constrain($class, $constraint)
297  {
298  $this->constraints[$class] = $constraint;
299  }
300 
301 
311  function excludeKeyFromJoin($class, $key)
312  {
313  if (!isset($this->excludedKeys[$class]))
314  {
315  $this->excludedKeys[$class] = array();
316  }
317 
318  $this->excludedKeys[$class][$key] = true;
319 
320  trace("*** Excluding $key on $class", 3);
321  return $this;
322  }
323 
330  function xref($xref, $xrefFieldMaps = null)
331  {
332  $x = new $xref;
333 
334  $this->xrefs[] = $x;
335 
336  if ($xrefFieldMaps)
337  {
338  $this->xrefFieldMaps[$x->table] = $xrefFieldMaps;
339  }
340  }
341 
349  function pseudo($class, $items, $indexField)
350  {
351  $this->pseudos[$class] = array("index" => $indexField, "items" => $items);
352  }
353 
354  abstract function generateQuery($constraint = "", $count = false);
355 
356  function count($constraints = "", $countExpr = true)
357  {
358  $query = $this->generateQuery($constraints, $countExpr);
359  try
360  {
362 
363  $result = $db->prepare($query);
364  $result->execute($this->params);
365 
366  if ($row = $result->fetch())
367  {
368  $value = $row['result'];
369  }
370 
371  unset($result);
372  }
373  catch(PDOException $e)
374  {
375  throw FakoliException($e->getMessage());
376  }
377 
378  return $value;
379  }
380 
387  function query($constraints = "", $page = -1, $size = -1)
388  {
389  $query = $this->generateQuery($constraints);
390 
391  try
392  {
394  $result = $db->prepare($query);
395  $result->execute();
396 
397  if ($page > 0)
398  {
399  $count = ($page - 1) * $size;
400  while($count--)
401  {
402  $result->fetch();
403  }
404  }
405 
406  $items = array();
407 
408  while($line = $result->fetch())
409  {
410  $item = new JoinResult($this->tag);
411 
412  for($i = 0; $i < count($this->classes); ++$i)
413  {
414  $class = $this->classes[$i];
415  $item->$class = new $class;
416  $item->$class->populate($line, $this->aliases[$item->$class->table]);
417 
418  foreach($this->pseudos as $class => $lookup)
419  {
420  foreach($this->aliases as $table => $prefix)
421  {
422  $keyAlias = "{$prefix}.{$lookup['index']}";
423 
424  if (!array_key_exists($keyAlias, $line)) continue;
425 
426  $key = $line[$keyAlias];
427 
428  //trace("$keyAlias = {$key}", 3);
429  //trace(print_r(array_keys($line), true), 3);
430 
431  if (array_key_exists($key, $lookup['items']))
432  {
433  //trace("Pseudo Match found for $keyAlias = $key",3);
434  $item->$class = $lookup['items'][$key];
435  break;
436  }
437  }
438  }
439 
440  --$size;
441  if ($size == 0) break;
442  }
443 
444  $items[] = $item;
445  }
446 
447  unset($result);
448 
449  return $items;
450  }
451  catch(PDOException $e)
452  {
453  throw new FakoliException($e->getMessage());
454  }
455  }
456 
462  {
463  return new JoinResultIterator($this, $constraints);
464  }
465 
475  function indexedQuery($constraints = "", $indexBy = "")
476  {
477  $field = "";
478  $idxClass = "";
479 
480  if ($indexBy != "")
481  {
482  list($idxClass, $field) = explode(".", $indexBy);
483  }
484  else
485  {
486  $idxClass = $this->class[0];
487  $obj = new $class;
488  $field = $obj->primary_key;
489  }
490 
491  $query = $this->generateQuery($constraints);
492 
493  try
494  {
496  $result = $db->prepare($query);
497  $result->execute();
498 
499  $items = array();
500 
501  while($line = $result->fetch())
502  {
503  $item = new JoinResult($this->tag);
504 
505  for($i = 0; $i < count($this->classes); ++$i)
506  {
507  $class = $this->classes[$i];
508  $item->$class = new $class;
509  $item->$class->populate($line, $this->aliases[$item->$class->table]);
510 
511  foreach($this->pseudos as $class => $lookup)
512  {
513  foreach($this->aliases as $table => $prefix)
514  {
515  $keyAlias = "{$prefix}.{$lookup['index']}";
516 
517  $key = $line[$keyAlias];
518 
519  if (array_key_exists($key, $lookup['items']))
520  {
521  $item->$class = $lookup['items'][$key];
522  break;
523  }
524  }
525  }
526  }
527 
528  $idx = $item->$idxClass->$field;
529  if (array_key_exists($idx, $items))
530  {
531  // Implicitly promote to array if there is a collision
532  if (!is_array($items[$idx]))
533  {
534  $items[$idx] = array($items[$idx]);
535  }
536  $items[$idx][] = $item;
537  }
538  else
539  {
540  $items[$idx] = $item;
541  }
542  }
543 
544  unset($result);
545 
546  return $items;
547  }
548  catch(PDOException $e)
549  {
550  throw new FakoliException($e->getMessage());
551  }
552  }
553 
560  function groupedQuery($constraints = "", $indexBy = "")
561  {
562  $field = "";
563  $idxClass = "";
564 
565  if ($indexBy != "")
566  {
567  list($idxClass, $field) = explode(".", $indexBy);
568  }
569  else
570  {
571  $idxClass = $this->class[0];
572  $obj = new $class;
573  $field = $obj->primary_key;
574  }
575 
576  trace("groupedQuery(): Group by $idxClass $field", 3);
577 
578  $query = $this->generateQuery($constraints);
579 
580  try
581  {
583  $result = $db->prepare($query);
584  $result->execute();
585 
586  $items = array();
587 
588  while($line = $result->fetch())
589  {
590  $item = new JoinResult($this->tag);
591 
592  for($i = 0; $i < count($this->classes); ++$i)
593  {
594  $class = $this->classes[$i];
595  $item->$class = new $class;
596  $item->$class->populate($line, $this->aliases[$item->$class->table]);
597 
598  foreach($this->pseudos as $class => $lookup)
599  {
600  foreach($this->aliases as $table => $prefix)
601  {
602  $keyAlias = "{$prefix}.{$lookup['index']}";
603 
604  $key = $line[$keyAlias];
605 
606  if (array_key_exists($key, $lookup['items']))
607  {
608  $item->$class = $lookup['items'][$key];
609  break;
610  }
611  }
612  }
613  }
614 
615  $idx = $item->$idxClass->$field;
616  $items[$idx][] = $item;
617  }
618 
619  unset($result);
620 
621  return $items;
622  }
623  catch(PDOException $e)
624  {
625  throw new FakoliException($e->getMessage());
626  }
627  }
628 
629 }
630 
647 class InnerJoin extends AbstractJoin
648 {
649 
653  function InnerJoin($tag = "join")
654  {
655  $this->tag = $tag;
656  }
657 
658  function generateQuery($constraints = "", $countOnly = false)
659  {
660  $tables = "";
661  $fields = "";
662  $alias = "";
663  for($i = 0; $i < count($this->classes); ++$i)
664  {
665  $class = $this->classes[$i];
666  $proto =& $this->protos[$class];
667 
668  $alias = "t$i";
669  $this->aliases[$proto->table] = $alias;
670 
671  if (array_key_exists($class, $this->constraints))
672  {
673  $c = $this->constraints[$class];
674 
675  foreach($proto->getFields() as $field => $type)
676  {
677  $c = preg_replace("/([\s\‍(])(".$field.")\\b/", "$1t$i.$2", $c);
678  }
679 
680  $c = "(".preg_replace("/^\\s*(WHERE\\s+|AND\\s+|OR\\s+)/i", "", $c).")";
681 
682  $this->constraints[$class] = $c;
683  }
684 
685  if ($tables) $tables .= ", ";
686  $tables .= "{$proto->table} $alias";
687 
688  $f = $proto->getFieldList($alias);
689  if ($fields && $f) $fields .=", ";
690  $fields .= $f;
691  }
692 
693  for($i = 0; $i < count($this->xrefs); ++$i)
694  {
695  $xref = $this->xrefs[$i];
696 
697  $alias = "x$i";
698  $this->xrefAliases[$xref->table] = $alias;
699 
700  if ($tables) $tables .= ", ";
701  $tables .= "{$xref->table} $alias";
702  }
703 
704  if ($this->unique) $fields = "DISTINCT $fields";
705 
706  $classConstraints = implode(" AND ", array_values($this->constraints));
707 
708  if (strncmp($constraints, "WHERE", 5))
709  {
710  if ($classConstraints) $classConstraints = " AND ".$classConstraints;
711  $constraints = "WHERE 1=1 $classConstraints $constraints";
712  }
713  else
714  {
715  if ($classConstraints) $classConstraints = $classConstraints." AND ";
716  $constraints = preg_replace("/^\\s*WHERE/i", "WHERE $classConstraints", $constraints);
717  }
718 
719  $join = "";
720 
721  // Build Inner Join
722 
723  for($i = 0; $i < count($this->classes) - 1; ++$i)
724  {
725  $c = $this->classes[$i];
726  $p = $this->protos[$c];
727 
728  for ($j = $i+1; $j < count($this->classes); ++$j)
729  {
730  $d = $this->classes[$j];
731  $q = $this->protos[$d];
732 
733  if ($this->excludedKeys[$d][$q->primary_key] ||
734  $this->excludedKeys[$d][$p->primary_key])
735  {
736  continue;
737  }
738 
739  trace($p->primary_key." ".array_key_exists($p->primary_key, $q->getFields()), 5);
740  trace($q->primary_key." ".array_key_exists($q->primary_key, $p->getFields()), 5);
741 
742  if (array_key_exists($p->primary_key, $q->getFields()))
743  {
744  $join .= " AND t$i.{$p->primary_key}=t$j.{$p->primary_key}";
745  }
746  else if (array_key_exists($q->primary_key, $p->getFields()))
747  {
748  $join .= " AND t$i.{$q->primary_key}=t$j.{$q->primary_key}";
749  }
750 
751  }
752 
753  }
754 
755  // Alias main table fields
756 
757  for($i = 0; $i < count($this->classes); ++$i)
758  {
759  $c = $this->classes[$i];
760  $p = $this->protos[$c];
761  foreach($p->getFields() as $field => $type)
762  {
763  $constraints = preg_replace("/([\s\‍(])(".$field.")\\b/", "$1t$i.$2", $constraints);
764  $constraints = preg_replace("/{$c}\.({$field})\\b/", "t$i.$1", $constraints);
765  }
766 
767  foreach($p->calculatedFields as $field => $expr)
768  {
769  foreach($p->getFields() as $f => $type)
770  {
771  $expr = preg_replace("/([\s\‍(])(".$f.")\\b/", "$1t$i.$2", $expr);
772  }
773 
774  $constraints = preg_replace("/([\s\‍(])(".$field.")\\b/", " $expr", $constraints);
775  }
776  }
777 
778  // Handle XRefs
779 
780  foreach($this->xrefs as $xref)
781  {
782  $map = $this->xrefFieldMaps[$xref->table];
783 
784  foreach($this->protos as $p)
785  {
786  $pk = $p->primary_key;
787  $xk = ($map && array_key_exists($pk, $map)) ? $map[$pk] : $pk;
788 
789  if ($xref->hasField($xk))
790  {
791  $xa = $this->xrefAliases[$xref->table];
792  $ta = $this->aliases[$p->table];
793 
794  $join .= " AND $xa.{$xk}=$ta.{$pk}";
795  }
796  }
797  }
798 
799  $matches = array();
800 
801  $orderBy = "";
802  if (preg_match("/^(.*?)(ORDER\\s+BY.*)/i", $constraints, $matches))
803  {
804  $constraints = $matches[1];
805  $orderBy = $matches[2];
806  }
807  else if (preg_match("/^(.*?)(LIMIT.*)/i", $constraints, $matches))
808  {
809  $constraints = $matches[1];
810  $orderBy = $matches[2];
811  }
812 
813  if ($countOnly) $fields = (is_bool($countOnly)) ? "COUNT(1) as result" : "COUNT($countOnly) as result";
814 
815  $query = "SELECT $fields FROM $tables $constraints $join $orderBy";
816 
817  trace("InnerJoin::generateQuery() - $query", 3);
818 
819  return $query;
820  }
821 }
822 
823 
824 
825 
826 
828 {
829 
833  function LeftOuterJoin($tag = "join")
834  {
835  $this->tag = $tag;
836  }
837 
838  function generateQuery($constraints = "", $countOnly = false)
839  {
840  $tables = "";
841  $fields = "";
842  $alias = "";
843 
844  trace(print_r($this->constraints, true), 3);
845 
846  $tableConstraints = array();
847  $whereConstraints = array();
848 
849  for($i = 0; $i < count($this->classes); ++$i)
850  {
851  $class = $this->classes[$i];
852  $proto =& $this->protos[$class];
853 
854  $alias = "t$i";
855  $this->aliases[$proto->table] = $alias;
856 
857  if ($tables) $tables .= ", ";
858  $tables .= "{$proto->table} $alias";
859 
860  if (array_key_exists($class, $this->constraints))
861  {
862  $c = $this->constraints[$class];
863 
864  foreach($proto->getFields() as $field => $type)
865  {
866  $c = preg_replace("/([\s\‍(])(".$field.")\\b/", "$1t$i.$2", $c);
867  }
868 
869  $c = "(".preg_replace("/^\\s*(WHERE\\s+|AND\\s+|OR\\s+)/i", "", $c).")";
870 
871  if ($i == 0)
872  {
873  // Constraint on first (anchor) class goes into where clause
874  $this->whereConstraints[$class] = $c;
875  }
876  else
877  {
878  // Constraints on other classes go into join clauses
879  $this->tableConstraints[$class] = $c;
880  }
881  }
882 
883  $f = $proto->getFieldList($alias);
884  if ($fields && $f) $fields .=", ";
885  $fields .= $f;
886  }
887 
888  for($i = 0; $i < count($this->xrefs); ++$i)
889  {
890  $xref = $this->xrefs[$i];
891 
892  $alias = "x$i";
893  $this->xrefAliases[$xref->table] = $alias;
894 
895  if ($tables) $tables .= ", ";
896  $tables .= "{$xref->table} $alias";
897  }
898 
899  if ($this->unique) $fields = "DISTINCT $fields";
900 
901  $classConstraints = implode(" AND ", array_values($this->whereConstraints));
902 
903  if (strncmp($constraints, "WHERE", 5))
904  {
905  if ($classConstraints) $classConstraints = " AND ".$classConstraints;
906  $constraints = "WHERE 1=1 $classConstraints $constraints";
907  }
908  else
909  {
910  if ($classConstraints) $classConstraints = $classConstraints." AND ";
911  $constraints = preg_replace("/^\\s*WHERE/i", "WHERE $classConstraints", $constraints);
912  }
913 
914  $join = "";
915  $join = $this->protos[$this->classes[0]]->table . " t0";
916 
917  // Build Left Outer Join
918 
919  $joined = array();
920 
921  for($i = 0; $i < count($this->classes) - 1; ++$i)
922  {
923  $c = $this->classes[$i];
924  $p = $this->protos[$c];
925 
926 
927  for ($j = $i+1; $j < count($this->classes); ++$j)
928  {
929  $d = $this->classes[$j];
930  $q = $this->protos[$d];
931 
932 
933 
934  trace($p->primary_key." ".array_key_exists($p->primary_key, $q->getFields()), 5);
935  trace($q->primary_key." ".array_key_exists($q->primary_key, $p->getFields()), 5);
936 
937  if (array_key_exists($p->primary_key, $q->getFields()))
938  {
939  if ($this->excludedKeys[$d][$p->primary_key] ||
940  $this->excludedKeys[$c][$p->primary_key])
941  {
942  continue;
943  }
944 
945  //$join .= " AND t$i.{$p->primary_key}=t$j.{$p->primary_key}";
946  $idx = "{$j}-{$p->primary_key}";
947  if ($joined[$idx])
948  {
949  continue;
950  }
951 
952  if ($joined[$j])
953  {
954  $join .= " AND t$i.{$p->primary_key}=t$j.{$p->primary_key}";
955  }
956  else
957  {
958  $join .= " LEFT OUTER JOIN {$q->table} t$j ON t$i.{$p->primary_key}=t$j.{$p->primary_key}";
959  if ($this->tableConstraints[$d])
960  {
961  $join .= " AND ".$this->tableConstraints[$d];
962  }
963  }
964 
965  $joined[$j] = true;
966  $joined[$idx] = true;
967 
968  }
969  else if (array_key_exists($q->primary_key, $p->getFields()))
970  {
971  if ($this->excludedKeys[$d][$q->primary_key] ||
972  $this->excludedKeys[$c][$q->primary_key])
973  {
974  continue;
975  }
976 
977  $idx = "{$j}-{$q->primary_key}";
978  if ($joined[$idx])
979  {
980  continue;
981  }
982 
983  //$join .= " AND t$i.{$q->primary_key}=t$j.{$q->primary_key}";
984  if ($joined[$j])
985  {
986  $join .= " AND t$i.{$q->primary_key}=t$j.{$q->primary_key}";
987  }
988  else
989  {
990  $join .= " LEFT OUTER JOIN {$q->table} t$j ON t$i.{$q->primary_key}=t$j.{$q->primary_key}";
991  }
992 
993  $joined[$j] = true;
994  $joined[$idx] = true;
995  }
996 
997  }
998 
999  }
1000 
1001  // Alias main table fields
1002 
1003  for($i = 0; $i < count($this->classes); ++$i)
1004  {
1005  $c = $this->classes[$i];
1006  $p = $this->protos[$c];
1007  foreach($p->getFields() as $field => $type)
1008  {
1009  $constraints = preg_replace("/([\s\‍(])(".$field.")\\b/", "$1t$i.$2", $constraints);
1010  }
1011 
1012  foreach($p->calculatedFields as $field => $expr)
1013  {
1014  foreach($p->getFields() as $f => $type)
1015  {
1016  $expr = preg_replace("/([\s\‍(])(".$f.")\\b/", "$1t$i.$2", $expr);
1017  }
1018 
1019  $constraints = preg_replace("/([\s\‍(])(".$field.")\\b/", " $expr", $constraints);
1020  }
1021  }
1022 
1023  // Handle XRefs
1024 
1025  foreach($this->xrefs as $xref)
1026  {
1027  $map = $this->xrefFieldMaps[$xref->table];
1028 
1029  foreach($this->protos as $p)
1030  {
1031  $pk = $p->primary_key;
1032  $xk = ($map && array_key_exists($pk, $map)) ? $map[$pk] : $pk;
1033 
1034  if ($xref->hasField($xk))
1035  {
1036  $xa = $this->xrefAliases[$xref->table];
1037  $ta = $this->aliases[$p->table];
1038 
1039  $join .= " LEFT OUTER JOIN {$xref->table} $xa ON $xa.{$xk}=$ta.{$pk}";
1040  }
1041  }
1042  }
1043 
1044  $matches = array();
1045 
1046  $orderBy = "";
1047  if (preg_match("/^(.*?)(ORDER\\s+BY.*)/i", $constraints, $matches))
1048  {
1049  $constraints = $matches[1];
1050  $orderBy = $matches[2];
1051  }
1052  else if (preg_match("/^(.*?)(LIMIT.*)/i", $constraints, $matches))
1053  {
1054  $constraints = $matches[1];
1055  $orderBy = $matches[2];
1056  }
1057 
1058  if ($countOnly) $fields = (is_bool($countOnly)) ? "COUNT(1) as result" : "COUNT($countOnly) as result";
1059 
1060  $query = "SELECT $fields FROM $join $constraints $orderBy";
1061 
1062  trace("LeftOuterJoin::generateQuery() - $query", 3);
1063 
1064  return $query;
1065  }
1066 
1067 }
1068 
1069 
1070 
1071 
1072 
1073 
1074 
1084 function extractJoinResults($class, $results)
1085 {
1086  $arr = array();
1087 
1088  if (!$results) return $arr;
1089 
1090  foreach($results as $result)
1091  {
1092  $arr[] = $result->$class;
1093  }
1094 
1095  return $arr;
1096 }
1097 
1098 function extractGroupedJoinResults($class, $results)
1099 {
1100  $arr = array();
1101 
1102  if (!$results) return $arr;
1103 
1104  foreach(array_keys($results) as $key)
1105  {
1106  $arr[$key] = extractJoinResults($class, $results[$key]);
1107  }
1108 
1109  return $arr;
1110 }?>
AbstractJoin is the abstract base class from which concrete join classes can be derived in order to p...
Definition: join.inc:233
excludeKeyFromJoin($class, $key)
By default joins are generated following the "most restrictive search" principle.
Definition: join.inc:311
$constraints
Class-specific constraints add via the constrain() method.
Definition: join.inc:242
pseudo($class, $items, $indexField)
Registers a class and indexed array as a pseudo-join item.
Definition: join.inc:349
groupedQuery($constraints="", $indexBy="")
Performs a query against the database, returning an array of arrays of DataItem objects of the specif...
Definition: join.inc:560
$xrefs
Cross-reference classes (i.e. classes that are part of the join, but not the result)
Definition: join.inc:236
add($class)
Adds a class to the join definition.
Definition: join.inc:256
$unique
true to output distinct rows only
Definition: join.inc:244
setFilter($class, $filter)
Sets an InclusionFilter or ExclusionFilter on the specified class.
Definition: join.inc:285
$tag
XML tag name.
Definition: join.inc:245
generateQuery($constraint="", $count=false)
count($constraints="", $countExpr=true)
Definition: join.inc:356
indexedQuery($constraints="", $indexBy="")
Performs a query against the database, returning an array of DataItem objects of the specified class,...
Definition: join.inc:475
$pseudos
Pseudo-join lookup tables.
Definition: join.inc:248
$xrefAliases
Cross-reference table aliases.
Definition: join.inc:241
iteratedQuery($constraints="")
Performs a query against the database returning an iterator of JoinResult objects.
Definition: join.inc:461
$aliases
Field aliases for output.
Definition: join.inc:240
$classes
The names of the DataItem classes being joined.
Definition: join.inc:234
$xrefFieldMaps
Cross-reference field map (to support cross-referencing through fields with differing names)
Definition: join.inc:238
xref($xref, $xrefFieldMaps=null)
Adds a cross-reference class to the join definition.
Definition: join.inc:330
constrain($class, $constraint)
Sets a query constraint on the given class.
Definition: join.inc:296
query($constraints="", $page=-1, $size=-1)
Performs a query against the database returning an array of JoinResult objects.
Definition: join.inc:387
$protos
Prototype instances of the DataItem classes being joined.
Definition: join.inc:235
static getConnection()
Retrieves a reference to the global database connection.
Used to place a filter on the contents of a DataItem-derived object.
This class is used to programmatically perform inner join queries across multiple objects.
Definition: join.inc:648
generateQuery($constraints="", $countOnly=false)
Definition: join.inc:658
InnerJoin($tag="join")
Create a new InnerJoin object.
Definition: join.inc:653
JoinResult is an empty placeholder class.
Definition: join.inc:46
format($template)
Formats the contents of the JoinResult using the supplied template.
Definition: join.inc:85
JoinResult($tag="join")
Definition: join.inc:50
$_classes
Definition: join.inc:48
toXML($indent=0)
Serializes the JoinResult to XML.
Definition: join.inc:61
$_xmlTagName
Definition: join.inc:47
__construct($join, $constraints)
Definition: join.inc:131
generateQuery($constraints="", $countOnly=false)
Definition: join.inc:838
LeftOuterJoin($tag="join")
Create a new LeftOuterJoin object.
Definition: join.inc:833
trace($msg, $lvl=3, $callStack=null)
Send output to the trace log.
Definition: functions.inc:1010
query($class)
Performs a query against the database, returning an array of DataItem objects of the specified class.
Definition: query.inc:373
extractGroupedJoinResults($class, $results)
Definition: join.inc:1098
extractJoinResults($class, $results)
Extracts the specified object from the combined join results, returning an array of the specified obj...
Definition: join.inc:1084