/**
  * Make where part of a SQL select to get children values
  * @param epDbObject $db the db connection  
  * @param epObject $o the child object for query
  * @param int $depth how many children down we are
  * @param string $parent the parent of this child
  * @return array('from', 'where')
  * @author Oak Nauhygon <*****@*****.**>
  * @author Trevan Richins <*****@*****.**>
  */
 public static function sqlSelectChildren($db, $o, $depth, $parent)
 {
     // array to keep new tables in 'from'
     $from = array();
     // array to keep new expression in 'where'
     $where = array();
     // get the class map for the child object
     $cm = $o->epGetClassMap();
     // get all vars in the object
     $vars = $o->epGetVars();
     // if object has oid, select use oid
     if ($oid = $o->epGetObjectId()) {
         $where[] = $db->quoteId($cm->getOidColumn()) . ' = ' . $oid;
         return array('from' => $from, 'where' => $where);
     }
     // mark child object under search (to avoid loops)
     $o->epSetSearching(true);
     // total number of vars (primitive or non-primitive) collected
     $n = 0;
     // new depth
     $depth++;
     // number of non-primitive (relationship) fields collected
     $nprim_id = 0;
     // loop through vars
     while (list($var, $val) = each($vars)) {
         // get field map
         if (!($fm =& $cm->getField($var))) {
             // should not happen
             continue;
         }
         // exclude null values (including empty strings)
         if (is_null($val) || !$val && $fm->getType() == epFieldMap::DT_CHAR) {
             continue;
         }
         // is it a primitive var?
         if ($fm->isPrimitive()) {
             $where[] = $db->quoteId($parent) . '.' . $db->quoteId($fm->getColumnName()) . ' = ' . $db->quote($val, $fm);
             // done for this var
             $n++;
             continue;
         }
         // okay we are dealing with a non-primitive (relationship) var
         if ($val instanceof epArray) {
             foreach ($val as $obj) {
                 // skip object that is under searching
                 if (!$obj || $obj->epIsSearching()) {
                     continue;
                 }
                 // get 'where' and 'from' from relationship
                 $from_where = epObj2Sql::sqlSelectRelations($db, $fm, $cm, $obj->epGetClassMap()->getTable(), $depth . $nprim_id, $parent);
                 $where = array_merge($where, $from_where['where']);
                 $from = array_merge($from, $from_where['from']);
                 // get 'where' and 'from' from relationship
                 $from_where = epObj2Sql::sqlSelectChildren($db, $obj, $depth, '_' . $depth . $nprim_id);
                 $where = array_merge($where, $from_where['where']);
                 $from = array_merge($from, $from_where['from']);
                 $nprim_id++;
             }
         } else {
             if ($val instanceof epObject && !$val->epIsSearching()) {
                 // get 'where' and 'from' from relationship
                 $from_where = epObj2Sql::sqlSelectRelations($db, $fm, $cm, $val->epGetClassMap()->getTable(), $depth . $nprim_id, $parent);
                 $where = array_merge($where, $from_where['where']);
                 $from = array_merge($from, $from_where['from']);
                 // get 'where' and 'from' from relationship
                 $from_where = epObj2Sql::sqlSelectChildren($db, $val, $depth, '_' . $depth . $nprim_id);
                 $where = array_merge($where, $from_where['where']);
                 $from = array_merge($from, $from_where['from']);
                 $nprim_id++;
             }
         }
         $n++;
     }
     // reset search flag on child object
     $o->epSetSearching(false);
     return array('from' => $from, 'where' => $where);
 }
 /**
  * Builds SQL statement from an object
  * @param epObject &$o the object 
  * @param epFieldMap &$fm the field map of the var that the object is assigned to
  * @param string $alias
  * @return array
  */
 protected function _buildSqlObject(epObject &$o, epFieldMap &$fm, $alias = self::DUMMY_ALIAS)
 {
     $o->epSetSearching(true);
     // get class map
     $cm = $this->em->getClassMap($fm->getClass());
     // for a committed object, simply use OID
     if ($oid = $o->epGetObjectId()) {
         $sql = $this->qid($alias) . '.' . $this->qid($cm->getOidColumn());
         $sql .= '=' . $this->q($oid);
         return array($sql);
     }
     // arrays to keep expressions for primitve and relationship vars
     $exprs_prm = array('');
     $exprs_rel = array('');
     // go through each var
     foreach ($o as $var => $val) {
         // get field map for var
         if (!($fm = $cm->getField($var))) {
             continue;
         }
         // primitive var
         if ($fm->isPrimitive() && !is_null($val)) {
             $expr = $this->qid($alias) . '.' . $this->qid($fm->getColumnName());
             $expr .= '=' . $this->q($val, $fm);
             $exprs_prm[] = $expr;
             continue;
         }
         // relationship var
         if (!$val) {
             // done if empty value or has been visited
             continue;
         }
         // is it a many-valued var?
         $vals = $val;
         if (!$fm->isMany()) {
             // arrayize it if not an array
             $vals = array($val);
         }
         // go through each value in array
         foreach ($vals as $val) {
             // build sql for the relationship field map
             $exprs_rf = $this->_buildSqlFieldMapRelationship($fm, $alias);
             // is it an error message?
             if (is_string($exprs_rf)) {
                 throw new epExceptionQueryBuilder($this->_e($exprs));
                 continue;
             }
             // concat sql for field map ($exprs_rf) and sql for var value ($exprs_rv)
             foreach ($exprs_rf as $alias_ => $expr_rf) {
                 // recursion: build sql for the relationship var
                 $exprs_rv = $this->_buildSqlObject($val, $fm, $alias_);
                 // collect expression for relationship var
                 foreach ($exprs_rv as $expr_rv) {
                     $exprs_rel[] = $expr_rf . ' AND ' . $expr_rv;
                 }
             }
         }
     }
     // array to hold final results
     $exprs_obj = array();
     // concat sql for primitive and relationship fields
     foreach ($exprs_rel as $expr_rel) {
         foreach ($exprs_prm as $expr_prm) {
             if (!$expr_rel && $expr_prm) {
                 $exprs_obj[] = $expr_prm;
             } else {
                 if ($expr_rel && !$expr_prm) {
                     $exprs_obj[] = $expr_rel;
                 } else {
                     if ($expr_rel && $expr_prm) {
                         $exprs_obj[] = $expr_rel . ' AND ' . $expr_prm;
                     }
                 }
             }
         }
     }
     $o->epSetSearching(false);
     return $exprs_obj;
 }