Framework  3.9
composite_data_item.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 require_once realpath(dirname(__FILE__))."/join.inc";
39 
41 {
43  var $__tx = null;
44 
45  function CompositeDataItem()
46  {
47  $__objects = array();
48  }
49 
50  function add()
51  {
52  $args = func_get_args();
53 
54  foreach($args as $cl)
55  {
56  $obj = new $cl();
57  $pk = $obj->getPrimaryKey();
58 
59  if (count($this->__objects))
60  {
61  $found = false;
62 
63  foreach($this->__objects as $o)
64  {
65  if ($o->hasField($pk))
66  {
67  $found = true;
68  break;
69  }
70  }
71 
72  if (!$found)
73  {
74  throw new DataItemException("$cl does not have a direct foreign key relationship with another class in ".get_class($this));
75  }
76  }
77 
78  $this->__objects[] = $obj;
79  $this->$cl = $obj;
80  }
81  }
82 
83  function cast($class)
84  {
85  if ($this->$class) return $this->$class;
86 
87  throw new DataItemException("Cannot cast from ".get_class($this)." to $class");
88  }
89 
94  function joinTransaction($tx)
95  {
96  foreach($this->__objects as $obj)
97  {
98  $obj->joinTransaction($tx);
99  }
100 
101  $this->__tx = $tx;
102  }
103 
107  function getTransaction()
108  {
109  return $this->__tx;
110  }
111 
120  function populate($line)
121  {
122  // Load all component objects from the outside in.
123 
124  $this->__objects[0]->populate($line);
125  $num = count($this->__objects);
126 
127  for($i = 1; $i < num; ++$i)
128  {
129  $pk = $this->__objects[$i];
130  $key = $obj->get($pk);
131  $obj->load($key);
132  }
133  }
134 
139  function getFieldList($alias = "")
140  {
141  $fields[] = array();
142 
143  // Create a composite field list from the inside out.
144  $num = count($this->__objects);
145  for($i = $num - 1; $i >= 0; ++$i)
146  {
147  $of = $this->__objects[$i]->getFields();
148  $fields = array_merge($fields, $of);
149  }
150 
151  return implode(", ", array_keys($fields));
152  }
153 
158  function getFieldArray()
159  {
160  $fields[] = array();
161 
162  // Create a composite field list from the inside out.
163  $num = count($this->__objects);
164  for($i = $num - 1; $i >= 0; --$i)
165  {
166  $of = $this->__objects[$i]->getFields();
167  $fields = array_merge($fields, $of);
168  }
169 
170  return array_keys($fields);
171  }
172 
176  function getFields()
177  {
178  $fields = array();
179  // Create a composite field list from the inside out.
180  $num = count($this->__objects);
181  for($i = $num - 1; $i >= 0; --$i)
182  {
183  $of = $this->__objects[$i]->getFields();
184  $fields = array_merge($fields, $of);
185  }
186 
187  return $fields;
188  }
189 
197  function hasField($field)
198  {
199  foreach($this->__objects as $obj)
200  {
201  if ($obj->hasField($field)) return true;
202  }
203 
204  return false;
205  }
206 
207  function getHiddenFields()
208  {
209  $hiddenFields = array();
210 
211  foreach($this->__objects as $obj)
212  {
213  $hidden = $obj->getHiddenFields();
214  if ($hidden) array_merge($hiddenFields, $hidden);
215  }
216 
217  return $hiddenFields;
218  }
219 
226  function hasRelation($relation)
227  {
228  foreach($this->__objects as $obj)
229  {
230  if ($obj->hasRelation($relation)) return true;
231  }
232 
233  return false;
234  }
235 
241  function findSubObject($field)
242  {
243  foreach($this->__objects as $obj)
244  {
245  if ($obj->hasField($field)) return $obj;
246  }
247 
248  throw new FakoliException("Cannot locate field '$field' in sub-objects");
249  }
250 
254  function getPrimaryKey()
255  {
256  // Primary key field for the composite object is the primary key field of the outermost object.
257 
258  return $this->__objects[0]->getPrimaryKey();
259  }
260 
265  function getPrimaryKeyList()
266  {
267  $pk = array();
268 
269  foreach($this->__objects as $obj)
270  {
271  $pk = array_merge($pk, $obj->getPrimaryKeyList());
272  }
273 
274  return $pk;
275  }
276 
282  function get($field)
283  {
284  foreach($this->__objects as $obj)
285  {
286  if ($obj->hasField($field)) return $obj->get($field);
287  }
288 
289  return null;
290  }
291 
297  function set($field, $value)
298  {
299  foreach($this->__objects as $obj)
300  {
301  if ($obj->hasField($field)) $obj->set($field, $value);
302  }
303  }
304 
310  function getType($field)
311  {
312  foreach($this->__objects as $obj)
313  {
314  if ($obj->hasField($field)) return $obj->getType($field);
315  }
316 
317  return null;
318  }
319 
323  function getFilter()
324  {
325  return $this->__objects[0]->getFilter();
326  }
327 
332  function setFilter($filter)
333  {
334  foreach($this->__objects as $obj)
335  {
336  $obj->setFilter($filter);
337  }
338  }
339 
344  function getFieldAliases()
345  {
346  $aliases = array();
347 
348  // Create a composite field list from the inside out.
349  $num = count($this->__objects);
350  for($i = $num - 1; $i >= 0; --$i)
351  {
352  $of = $this->__objects[$i]->getFieldAliases();
353  if ($of)
354  {
355  $aliases = array_merge($aliases, $of);
356  }
357  }
358 
359  return $aliases;
360  }
361 
367  {
368  $aliases = array();
369 
370  // Create a composite field list from the inside out.
371  $num = count($this->__objects);
372  for($i = $num - 1; $i >= 0; --$i)
373  {
374  $of = $this->__objects[$i]->getFieldAnnotations();
375  if ($of)
376  {
377  $aliases = array_merge($aliases, $of);
378  }
379  }
380 
381  return $aliases;
382  }
383 
389  function load($id)
390  {
391  // Load from the outside in, chaining on the
392  // primary key/foreign key relationship at each level
393 
394  $num = count($this->__objects);
395 
396  for($i = 0; $i < $num; ++$i)
397  {
398  $this->__objects[$i]->load($id);
399 
400  if ($i == $num - 1) continue;
401 
402  $id = $this->__objects[$i]->get($this->__objects[$i+1]->getPrimaryKey());
403  }
404  }
405 
406  function loadFromBase($id)
407  {
408  $num = count($this->__objects);
409 
410  $this->__objects[$num - 1]->load($id);
411 
412  for($i = $num - 2; $i >= 0; --$i)
413  {
414  trace("BOOGER!", 3);
415  $pk = $this->__objects[$i+1]->getPrimaryKey();
416  $cl = get_class($this->__objects[$i]);
417  $obj = querySingle($cl, "WHERE $pk=".$this->__objects[$i+1]->get($pk));
418  $this->__objects[$i] = $obj;
419  $this->$cl = $obj;
420  }
421  }
422 
426  function save()
427  {
428  // Save from the inside out, chaining on the
429  // primary key/foreign key relationship at each level
430  $num = count($this->__objects);
431 
432  for($i = $num - 1; $i >= 0; --$i)
433  {
434  if ($this->__objects[$i]->hasField("composite_class"))
435  {
436  $this->__objects[$i]->set("composite_class", get_class($this));
437  }
438 
439  $this->__objects[$i]->save();
440  if ($i != 0)
441  {
442  $pk = $this->__objects[$i]->getPrimaryKey();
443  $this->__objects[$i - 1]->set($pk, $this->__objects[$i]->get($pk));
444  }
445  }
446  }
447 
451  function select()
452  {
453  // Select from the outside in, chaining on the
454  // primary key/foreign key relationship at each level
455 
456  $this->__objects[0]->select();
457 
458  $num = count($this->__objects);
459 
460  for($i = 1; $i < $num; $i)
461  {
462  $pk = $this->__objects[$i]->getPrimaryKey();
463  $this->__objects[$i]->set($pk, $this->__objects[$i - 1]->get($pk));
464  $this->__objects[$i]->select();
465  }
466  }
467 
471  function exists($constraint = "")
472  {
473  return $this->__objects[0]->exists($constraint);
474  }
475 
479  function update()
480  {
481  foreach($this->__objects as $obj)
482  {
483  $obj->update();
484  }
485  }
486 
490  function insert()
491  {
492  // Insert and chain primary/foreign keys, from the inside out
493 
494  $num = count($this->__objects);
495 
496  for($i = $num - 1; $i >= 0; --$i)
497  {
498  $this->__objects[$i]->insert();
499  if ($i > 0)
500  {
501  $pk = $this->__objects[$i]->getPrimaryKey();
502  $this->__objects[$i - 1]->set($pk, $this->__objects[$i]->get($pk));
503  }
504  }
505  }
506 
510  function delete($constraint = "")
511  {
512  if ($constraint)
513  {
514  throw new DataItemException("CompositeDataItem::delete() does not currently support constraint clauses");
515  }
516 
517  $num = count($this->__objects);
518 
519  for($i = 0; $i < $num; ++$i)
520  {
521  $this->__objects[$i]->delete();
522  }
523  }
524 
528  function deleteAll()
529  {
530  for($i = 0; $i < $num; ++$i)
531  {
532  $this->__objects[$i]->deleteAll();
533  }
534  }
535 
544  function distinctValues($field, $sorted = false, $constraint = "")
545  {
546  foreach($this->__objects as $obj)
547  {
548  if ($obj->hasField($field))
549  {
550  return $obj->distinctValues($field, $sorted, $constraint);
551  }
552  }
553 
554  throw new DataItemException("Could not find field '$field' in any component objects");
555  }
556 
560  function fromGET()
561  {
562  foreach($this->__objects as $obj)
563  {
564  $obj->fromGET();
565  }
566  }
567 
574  function fromPOST()
575  {
576  foreach($this->__objects as $obj)
577  {
578  $obj->fromPOST();
579  }
580  }
581 
586  function fromREQUEST()
587  {
588  foreach($this->__objects as $obj)
589  {
590  $obj->fromREQUEST();
591  }
592  }
593 
601  function compare($to)
602  {
603  if (get_class($to) != get_class($this)) return false;
604  $num = count($this->__objects);
605 
606  for($i = 0; $i < $num; ++$i)
607  {
608  if (!$this->__objects[$i]->compare($to->__objects[$i])) return false;
609  }
610 
611  return true;
612  }
613 
620  function copy($from)
621  {
622  if (get_class($to) != get_class($this)) return false;
623 
624  $num = count($this->__objects);
625 
626  for($i = 0; $i < $num; ++$i)
627  {
628  $this->__objects[$i]->copy($from->__objects[$i]);
629  }
630  }
631 
637  function toXML($indent = 0, $path = null)
638  {
639  $xml = str_repeat(" ", $indent) . "<" . get_class($this) . ">\n";
640  foreach ($this as $field => $val)
641  {
642  if (is_object($val))
643  {
644  $xml .= $val->toXML($indent + 1);
645  }
646  }
647 
648  $xml .= str_repeat(" ", $indent) . "</" . get_class($this) . ">\n";
649 
650  return $xml;
651  }
652 
653 
654  function fromXML($node)
655  {
656  //TODO: Implement this
657  }
658 
659  function format($template = "", $separator = ",")
660  {
661  $matches = array();
662 
663  preg_match_all("/\\{([\\w_]+)\.([^}]+)}/", $template, $matches, PREG_SET_ORDER);
664 
665  foreach($matches as $match)
666  {
667  $class = $match[1];
668  $subtemplate = $match[2];
669 
670  $value = $this->$class->format("{".$subtemplate."}", $separator);
671 
672  $template = str_replace($match[0], $value, $template);
673  }
674 
675  return $template;
676  }
677 
678 
679  function prettifyFieldName($field)
680  {
681  foreach($this->__objects as $obj)
682  {
683  if ($obj->hasField($field)) return $obj->prettifyFieldName($field);
684  }
685 
686  return $field;
687  }
688 
689 
690  function prettifyClassName($plural = false)
691  {
692  $c = ($this->pretty_class_name) ? $this->pretty_class_name : get_class($this);
693  $c = preg_replace(array("/([a-z])([A-Z0-9])/",
694  "/_/"),
695  array("$1 $2",
696  " "),
697  $c);
698  $c = ucwords($c);
699 
700  if ($plural)
701  {
702  pluralize($c);
703  }
704 
705  return $c;
706  }
707 
716  function relateTo($target, $field = "")
717  {
718  if ($field == "") $field = $target->getPrimaryKey();
719 
720  foreach($this->__objects as $obj)
721  {
722  if ($obj->hasField($field))
723  {
724  $obj->relateTo($target, $field);
725  return;
726  }
727  }
728  }
729 
730 
731  function query($constraints = "", $page = -1, $size = -1)
732  {
733  $join = new InnerJoin();
734  foreach($this->__objects as $obj)
735  {
736  $join->add(get_class($obj));
737  }
738 
739  $joinResults = $join->query($constraints, $page, $size);
740 
741  $results = array();
742 
743  foreach($joinResults as $result)
744  {
745  $obj = clone($this);
746  foreach($obj->__objects as $target)
747  {
748  $cl = get_class($target);
749  $target->copy($result->$cl);
750  }
751 
752  $results[] = $obj;
753  }
754 
755  return $results;
756  }
757 
758 
768  function indexedQuery($constraints = "", $indexBy = "")
769  {
770  $field = "";
771  $idxClass = "";
772 
773  if ($indexBy != "")
774  {
775  list($idxClass, $field) = explode(".", $indexBy);
776  }
777  else
778  {
779  $idxClass = $this->class[0];
780  $obj = new $class;
781  $field = $obj->primary_key;
782  }
783 
784  $query = $this->generateQuery($constraints);
785 
786  try
787  {
789  $result = $db->prepare($query);
790  $result->execute();
791 
792  $items = array();
793 
794  while($line = $result->fetch())
795  {
796  $item = new JoinResult($this->tag);
797 
798  for($i = 0; $i < count($this->classes); ++$i)
799  {
800  $class = $this->classes[$i];
801  $item->$class = new $class; //Hack to work around PHP's stupid implementation of get_class()
802  $item->$class->populate($line);
803  }
804 
805  $idx = $item->$idxClass->$field;
806  if (array_key_exists($idx, $items))
807  {
808  // Implicitly promote to array if there is a collision
809  if (!is_array($items[$idx]))
810  {
811  $items[$idx] = array($items[$idx]);
812  }
813  $items[$idx][] = $item;
814  }
815  else
816  {
817  $items[$idx] = $item;
818  }
819  }
820 
821  unset($result);
822 
823  return $items;
824  }
825  catch(PDOException $e)
826  {
827  die("InnerJoin::indexedQuery() failed - ".$e->getMessage());
828  }
829  }
830 
837  function groupedQuery($constraints = "", $indexBy = "")
838  {
839  $field = "";
840  $idxClass = "";
841 
842  if ($indexBy != "")
843  {
844  list($idxClass, $field) = explode(".", $indexBy);
845  }
846  else
847  {
848  $idxClass = $this->class[0];
849  $obj = new $class;
850  $field = $obj->primary_key;
851  }
852 
853  trace("InnerJoin::groupedQuery(): Group by $idxClass $field", 3);
854 
855  $query = $this->generateQuery($constraints);
856 
857  try
858  {
860  $result = $db->prepare($query);
861  $result->execute();
862 
863  $items = array();
864 
865  while($line = $result->fetch())
866  {
867  $item = new JoinResult($this->tag);
868 
869  for($i = 0; $i < count($this->classes); ++$i)
870  {
871  $class = $this->classes[$i];
872  $item->$class = new $class; //Hack to work around PHP's stupid implementation of get_class()
873  $item->$class->populate($line);
874  }
875 
876  $idx = $item->$idxClass->$field;
877  $items[$idx][] = $item;
878  }
879 
880  unset($result);
881 
882  return $items;
883  }
884  catch(PDOException $e)
885  {
886  die("InnerJoin::groupedQuery() failed - ".$e->getMessage());
887  }
888  }
889 
890 }
891 ?>
Abstract base class for all DataItem implementations.
getPrimaryKeyList()
Retrieves a list of all the primary keys used for an object as an array.
findSubObject($field)
Finds and returns the sub-object that contains the given field.
deleteAll()
Delete all the rows in the database that correspond to this class.
groupedQuery($constraints="", $indexBy="")
Performs a query against the database, returning an array of arrays of CompositeDataItem objects of t...
getFieldAliases()
Retrieve the list of field aliases.
getHiddenFields()
Retrieves the list of hidden fields.
fromXML($node)
Populates the object from the specified XML node.
hasRelation($relation)
Returns true if this DataItem contains a relation with the specified name.
populate($line)
Populates the object using the supplied associative array (field -> value).
getType($field)
Retrieves the data type of the specified field.
hasField($field)
Returns true if this DataItem contains a field with the specified name and that field is not excluded...
fromREQUEST()
Automatically populate the object based on parameters in either the $_GET or $_POST collection,...
indexedQuery($constraints="", $indexBy="")
Performs a query against the database, returning an array of CompositeDataItem objects of the specifi...
copy($from)
Copies values from another object, field by field.
fromPOST()
Automatically populate the object based on parameters in the $_POST collection.
load($id)
Load the object with the specified primary key.
fromGET()
Automatically populate the object based on parameters in the $_GET collection.
getPrimaryKey()
Retrieves the primary key field name.
relateTo($target, $field="")
Link this object to the specified target by setting corresponding field to the value of the target's ...
format($template="", $separator=",")
getFieldAnnotations()
Retrieve the list of field annotations.
getFieldArray()
Return an array of field names for this object filtered by any active filter.
query($constraints="", $page=-1, $size=-1)
getFieldList($alias="")
Returned a comma-separated list of the fields for this object (applying the assigned filter if there ...
prettifyClassName($plural=false)
distinctValues($field, $sorted=false, $constraint="")
Retrieves the distinct values in the database for the specified field across the specified set of rec...
compare($to)
Compare this object to another object.
getFilter()
Returns the filter set on this object.
cast($class)
Cast this object to another class.
setFilter($filter)
Sets the filter on this object.
insert()
Insert a new row in the database to store this object.
update()
Update the row in the database that corresponds to this object.
joinTransaction($tx)
Join the DataItem to the specified DataTransaction.
getTransaction()
Retrieves the current DataTransaction.
select()
Select the object from the database, based on the value of the primary key field.
getFields()
Retrieve the field type list for this object.
toXML($indent=0, $path=null)
Generates an XML representation of the object.
save()
Store the object in the database.
exists($constraint="")
Check whether the object exists in the database.
static getConnection()
Retrieves a reference to the global database connection.
This class is used to programmatically perform inner join queries across multiple objects.
Definition: join.inc:648
JoinResult is an empty placeholder class.
Definition: join.inc:46
trace($msg, $lvl=3, $callStack=null)
Send output to the trace log.
Definition: functions.inc:1010
pluralize($text, $count=0)
Takes a singular string and makes it plural.
Definition: functions.inc:1428
querySingle($class)
Performs a query against the database and returns a matching singleton object.
Definition: data_item.inc:1810