37 require_once realpath(dirname(__FILE__).
"/data_item.inc");
52 $this->_xmlTagName = $tag;
53 $this->_classes = array();
63 $xml = str_repeat(
" ", $indent) .
"<" . $this->_xmlTagName .
">\n";
64 foreach ($this as $field => $val)
68 $xml .= $val->toXML($indent + 1);
72 $xml .= str_repeat(
" ", $indent) .
"</" . $this->_xmlTagName .
">\n";
89 if (count($this->_classes) == 1 && preg_match(
"/^[\\w_]+/"))
91 $cl = $this->_classes[0];
92 return $this->$cl->$format;
95 preg_match_all(
"/\\{([\\w_]+)\.([^}]+)}/", $template, $matches, PREG_SET_ORDER);
97 foreach($matches as $match)
100 $subtemplate = $match[2];
102 $value = is_object($this->$class) ? $this->$class->format(
"{".$subtemplate.
"}") :
"";
104 $template = str_replace($match[0], $value, $template);
109 preg_match_all(
"/\\{([\\w\\d_]+)::([^}]*)}/", $template, $matches, PREG_SET_ORDER);
111 foreach($matches as $match)
113 $sub = call_user_func(array($match[1], $match[2]), $this);
114 $template = str_replace($match[0], $sub, $template);
138 $class =
$join->classes[$i];
139 $this->item->$class =
new $class;
148 trace(
"Number of Rows: {$this->rowCount}", 4);
150 return $this->rowCount;
155 if ($this->position == 0)
return;
162 $this->
query = $this->join->generateQuery($this->constraints);
168 $this->result = $db->prepare($this->
query);
169 $this->result->execute();
170 $this->rowCount = $this->result->rowCount();
171 $this->
current = $this->result->fetch();
173 catch(PDOException $e)
175 throw new FakoliException($e->getMessage());
181 for($i = 0; $i <
count($this->join->classes); ++$i)
183 $class = $this->join->classes[$i];
184 $this->item->$class->populate($this->
current, $this->join->aliases[$this->item->$class->table]);
187 foreach($this->join->pseudos as $class => $lookup)
189 foreach($this->join->aliases as $table => $prefix)
191 $keyAlias =
"{$prefix}.{$lookup['index']}";
193 $key = $this->
current[$keyAlias];
195 if (array_key_exists($key, $lookup[
'items']))
197 $this->item->$class = $lookup[
'items'][$key];
208 return ($this->
current !=
null);
213 $this->
current = $this->result->fetch();
258 $this->classes[] = $class;
260 $this->protos[$class] =
new $class;
262 if (func_num_args() > 1)
266 if (func_get_arg(1) !==
false)
268 for($i = 1; $i < func_num_args(); ++$i)
270 $filter->add(func_get_arg($i));
274 $this->protos[$class]->filter = $filter;
287 $this->protos[$class]->filter = $filter;
298 $this->constraints[$class] = $constraint;
313 if (!isset($this->excludedKeys[$class]))
315 $this->excludedKeys[$class] = array();
318 $this->excludedKeys[$class][$key] =
true;
320 trace(
"*** Excluding $key on $class", 3);
349 function pseudo($class, $items, $indexField)
351 $this->pseudos[$class] = array(
"index" => $indexField,
"items" => $items);
363 $result = $db->prepare($query);
364 $result->execute($this->params);
366 if ($row = $result->fetch())
368 $value = $row[
'result'];
373 catch(PDOException $e)
375 throw FakoliException($e->getMessage());
394 $result = $db->prepare($query);
399 $count = ($page - 1) * $size;
408 while($line = $result->fetch())
412 for($i = 0; $i <
count($this->classes); ++$i)
414 $class = $this->classes[$i];
415 $item->$class =
new $class;
416 $item->$class->populate($line, $this->aliases[$item->$class->table]);
418 foreach($this->pseudos as $class => $lookup)
420 foreach($this->aliases as $table => $prefix)
422 $keyAlias =
"{$prefix}.{$lookup['index']}";
424 if (!array_key_exists($keyAlias, $line))
continue;
426 $key = $line[$keyAlias];
431 if (array_key_exists($key, $lookup[
'items']))
434 $item->$class = $lookup[
'items'][$key];
441 if ($size == 0)
break;
451 catch(PDOException $e)
453 throw new FakoliException($e->getMessage());
482 list($idxClass, $field) = explode(
".", $indexBy);
486 $idxClass = $this->
class[0];
488 $field = $obj->primary_key;
496 $result = $db->prepare($query);
501 while($line = $result->fetch())
505 for($i = 0; $i <
count($this->classes); ++$i)
507 $class = $this->classes[$i];
508 $item->$class =
new $class;
509 $item->$class->populate($line, $this->aliases[$item->$class->table]);
511 foreach($this->pseudos as $class => $lookup)
513 foreach($this->aliases as $table => $prefix)
515 $keyAlias =
"{$prefix}.{$lookup['index']}";
517 $key = $line[$keyAlias];
519 if (array_key_exists($key, $lookup[
'items']))
521 $item->$class = $lookup[
'items'][$key];
528 $idx = $item->$idxClass->$field;
529 if (array_key_exists($idx, $items))
532 if (!is_array($items[$idx]))
534 $items[$idx] = array($items[$idx]);
536 $items[$idx][] = $item;
540 $items[$idx] = $item;
548 catch(PDOException $e)
550 throw new FakoliException($e->getMessage());
567 list($idxClass, $field) = explode(
".", $indexBy);
571 $idxClass = $this->
class[0];
573 $field = $obj->primary_key;
576 trace(
"groupedQuery(): Group by $idxClass $field", 3);
583 $result = $db->prepare($query);
588 while($line = $result->fetch())
592 for($i = 0; $i <
count($this->classes); ++$i)
594 $class = $this->classes[$i];
595 $item->$class =
new $class;
596 $item->$class->populate($line, $this->aliases[$item->$class->table]);
598 foreach($this->pseudos as $class => $lookup)
600 foreach($this->aliases as $table => $prefix)
602 $keyAlias =
"{$prefix}.{$lookup['index']}";
604 $key = $line[$keyAlias];
606 if (array_key_exists($key, $lookup[
'items']))
608 $item->$class = $lookup[
'items'][$key];
615 $idx = $item->$idxClass->$field;
616 $items[$idx][] = $item;
623 catch(PDOException $e)
625 throw new FakoliException($e->getMessage());
663 for($i = 0; $i <
count($this->classes); ++$i)
665 $class = $this->classes[$i];
666 $proto =& $this->protos[$class];
669 $this->aliases[$proto->table] = $alias;
671 if (array_key_exists($class, $this->constraints))
673 $c = $this->constraints[$class];
675 foreach($proto->getFields() as $field => $type)
677 $c = preg_replace(
"/([\s\(])(".$field.
")\\b/",
"$1t$i.$2", $c);
680 $c =
"(".preg_replace(
"/^\\s*(WHERE\\s+|AND\\s+|OR\\s+)/i",
"", $c).
")";
682 $this->constraints[$class] = $c;
685 if ($tables) $tables .=
", ";
686 $tables .=
"{$proto->table} $alias";
688 $f = $proto->getFieldList($alias);
689 if ($fields && $f) $fields .=
", ";
693 for($i = 0; $i <
count($this->xrefs); ++$i)
695 $xref = $this->xrefs[$i];
698 $this->xrefAliases[$xref->table] = $alias;
700 if ($tables) $tables .=
", ";
701 $tables .=
"{$xref->table} $alias";
704 if ($this->unique) $fields =
"DISTINCT $fields";
706 $classConstraints = implode(
" AND ", array_values($this->constraints));
710 if ($classConstraints) $classConstraints =
" AND ".$classConstraints;
711 $constraints =
"WHERE 1=1 $classConstraints $constraints";
715 if ($classConstraints) $classConstraints = $classConstraints.
" AND ";
723 for($i = 0; $i <
count($this->classes) - 1; ++$i)
725 $c = $this->classes[$i];
726 $p = $this->protos[$c];
728 for ($j = $i+1; $j <
count($this->classes); ++$j)
730 $d = $this->classes[$j];
731 $q = $this->protos[$d];
733 if ($this->excludedKeys[$d][$q->primary_key] ||
734 $this->excludedKeys[$d][$p->primary_key])
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);
742 if (array_key_exists($p->primary_key, $q->getFields()))
744 $join .=
" AND t$i.{$p->primary_key}=t$j.{$p->primary_key}";
746 else if (array_key_exists($q->primary_key, $p->getFields()))
748 $join .=
" AND t$i.{$q->primary_key}=t$j.{$q->primary_key}";
757 for($i = 0; $i <
count($this->classes); ++$i)
759 $c = $this->classes[$i];
760 $p = $this->protos[$c];
761 foreach($p->getFields() as $field => $type)
767 foreach($p->calculatedFields as $field => $expr)
769 foreach($p->getFields() as $f => $type)
771 $expr = preg_replace(
"/([\s\(])(".$f.
")\\b/",
"$1t$i.$2", $expr);
780 foreach($this->xrefs as $xref)
782 $map = $this->xrefFieldMaps[$xref->table];
784 foreach($this->protos as $p)
786 $pk = $p->primary_key;
787 $xk = ($map && array_key_exists($pk, $map)) ? $map[$pk] : $pk;
789 if ($xref->hasField($xk))
791 $xa = $this->xrefAliases[$xref->table];
792 $ta = $this->aliases[$p->table];
794 $join .=
" AND $xa.{$xk}=$ta.{$pk}";
802 if (preg_match(
"/^(.*?)(ORDER\\s+BY.*)/i",
$constraints, $matches))
805 $orderBy = $matches[2];
807 else if (preg_match(
"/^(.*?)(LIMIT.*)/i",
$constraints, $matches))
810 $orderBy = $matches[2];
813 if ($countOnly) $fields = (is_bool($countOnly)) ?
"COUNT(1) as result" :
"COUNT($countOnly) as result";
815 $query =
"SELECT $fields FROM $tables $constraints $join $orderBy";
817 trace(
"InnerJoin::generateQuery() - $query", 3);
844 trace(print_r($this->constraints,
true), 3);
846 $tableConstraints = array();
847 $whereConstraints = array();
849 for($i = 0; $i <
count($this->classes); ++$i)
851 $class = $this->classes[$i];
852 $proto =& $this->protos[$class];
855 $this->aliases[$proto->table] = $alias;
857 if ($tables) $tables .=
", ";
858 $tables .=
"{$proto->table} $alias";
860 if (array_key_exists($class, $this->constraints))
862 $c = $this->constraints[$class];
864 foreach($proto->getFields() as $field => $type)
866 $c = preg_replace(
"/([\s\(])(".$field.
")\\b/",
"$1t$i.$2", $c);
869 $c =
"(".preg_replace(
"/^\\s*(WHERE\\s+|AND\\s+|OR\\s+)/i",
"", $c).
")";
874 $this->whereConstraints[$class] = $c;
879 $this->tableConstraints[$class] = $c;
883 $f = $proto->getFieldList($alias);
884 if ($fields && $f) $fields .=
", ";
888 for($i = 0; $i <
count($this->xrefs); ++$i)
890 $xref = $this->xrefs[$i];
893 $this->xrefAliases[$xref->table] = $alias;
895 if ($tables) $tables .=
", ";
896 $tables .=
"{$xref->table} $alias";
899 if ($this->unique) $fields =
"DISTINCT $fields";
901 $classConstraints = implode(
" AND ", array_values($this->whereConstraints));
905 if ($classConstraints) $classConstraints =
" AND ".$classConstraints;
906 $constraints =
"WHERE 1=1 $classConstraints $constraints";
910 if ($classConstraints) $classConstraints = $classConstraints.
" AND ";
915 $join = $this->protos[$this->classes[0]]->table .
" t0";
921 for($i = 0; $i <
count($this->classes) - 1; ++$i)
923 $c = $this->classes[$i];
924 $p = $this->protos[$c];
927 for ($j = $i+1; $j <
count($this->classes); ++$j)
929 $d = $this->classes[$j];
930 $q = $this->protos[$d];
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);
937 if (array_key_exists($p->primary_key, $q->getFields()))
939 if ($this->excludedKeys[$d][$p->primary_key] ||
940 $this->excludedKeys[$c][$p->primary_key])
946 $idx =
"{$j}-{$p->primary_key}";
954 $join .=
" AND t$i.{$p->primary_key}=t$j.{$p->primary_key}";
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])
961 $join .=
" AND ".$this->tableConstraints[$d];
966 $joined[$idx] =
true;
969 else if (array_key_exists($q->primary_key, $p->getFields()))
971 if ($this->excludedKeys[$d][$q->primary_key] ||
972 $this->excludedKeys[$c][$q->primary_key])
977 $idx =
"{$j}-{$q->primary_key}";
986 $join .=
" AND t$i.{$q->primary_key}=t$j.{$q->primary_key}";
990 $join .=
" LEFT OUTER JOIN {$q->table} t$j ON t$i.{$q->primary_key}=t$j.{$q->primary_key}";
994 $joined[$idx] =
true;
1003 for($i = 0; $i <
count($this->classes); ++$i)
1005 $c = $this->classes[$i];
1006 $p = $this->protos[$c];
1007 foreach($p->getFields() as $field => $type)
1012 foreach($p->calculatedFields as $field => $expr)
1014 foreach($p->getFields() as $f => $type)
1016 $expr = preg_replace(
"/([\s\(])(".$f.
")\\b/",
"$1t$i.$2", $expr);
1025 foreach($this->xrefs as $xref)
1027 $map = $this->xrefFieldMaps[$xref->table];
1029 foreach($this->protos as $p)
1031 $pk = $p->primary_key;
1032 $xk = ($map && array_key_exists($pk, $map)) ? $map[$pk] : $pk;
1034 if ($xref->hasField($xk))
1036 $xa = $this->xrefAliases[$xref->table];
1037 $ta = $this->aliases[$p->table];
1039 $join .=
" LEFT OUTER JOIN {$xref->table} $xa ON $xa.{$xk}=$ta.{$pk}";
1047 if (preg_match(
"/^(.*?)(ORDER\\s+BY.*)/i",
$constraints, $matches))
1050 $orderBy = $matches[2];
1052 else if (preg_match(
"/^(.*?)(LIMIT.*)/i",
$constraints, $matches))
1055 $orderBy = $matches[2];
1058 if ($countOnly) $fields = (is_bool($countOnly)) ?
"COUNT(1) as result" :
"COUNT($countOnly) as result";
1060 $query =
"SELECT $fields FROM $join $constraints $orderBy";
1062 trace(
"LeftOuterJoin::generateQuery() - $query", 3);
1088 if (!$results)
return $arr;
1090 foreach($results as $result)
1092 $arr[] = $result->$class;
1102 if (!$results)
return $arr;
1104 foreach(array_keys($results) as $key)
AbstractJoin is the abstract base class from which concrete join classes can be derived in order to p...
excludeKeyFromJoin($class, $key)
By default joins are generated following the "most restrictive search" principle.
$constraints
Class-specific constraints add via the constrain() method.
pseudo($class, $items, $indexField)
Registers a class and indexed array as a pseudo-join item.
groupedQuery($constraints="", $indexBy="")
Performs a query against the database, returning an array of arrays of DataItem objects of the specif...
$xrefs
Cross-reference classes (i.e. classes that are part of the join, but not the result)
add($class)
Adds a class to the join definition.
$unique
true to output distinct rows only
setFilter($class, $filter)
Sets an InclusionFilter or ExclusionFilter on the specified class.
generateQuery($constraint="", $count=false)
count($constraints="", $countExpr=true)
indexedQuery($constraints="", $indexBy="")
Performs a query against the database, returning an array of DataItem objects of the specified class,...
$pseudos
Pseudo-join lookup tables.
$xrefAliases
Cross-reference table aliases.
iteratedQuery($constraints="")
Performs a query against the database returning an iterator of JoinResult objects.
$aliases
Field aliases for output.
$classes
The names of the DataItem classes being joined.
$xrefFieldMaps
Cross-reference field map (to support cross-referencing through fields with differing names)
xref($xref, $xrefFieldMaps=null)
Adds a cross-reference class to the join definition.
constrain($class, $constraint)
Sets a query constraint on the given class.
query($constraints="", $page=-1, $size=-1)
Performs a query against the database returning an array of JoinResult objects.
$protos
Prototype instances of the DataItem classes being joined.
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.
generateQuery($constraints="", $countOnly=false)
InnerJoin($tag="join")
Create a new InnerJoin object.
JoinResult is an empty placeholder class.
format($template)
Formats the contents of the JoinResult using the supplied template.
toXML($indent=0)
Serializes the JoinResult to XML.
__construct($join, $constraints)
generateQuery($constraints="", $countOnly=false)
LeftOuterJoin($tag="join")
Create a new LeftOuterJoin object.
trace($msg, $lvl=3, $callStack=null)
Send output to the trace log.
query($class)
Performs a query against the database, returning an array of DataItem objects of the specified class.
extractGroupedJoinResults($class, $results)
extractJoinResults($class, $results)
Extracts the specified object from the combined join results, returning an array of the specified obj...