protected function buildRecord($row) { $joined = array(); foreach ($row as $key => $value) { if (is_null($value)) { unset($row[$key]); } else { $parts = explode('.', $key, 2); if (count($parts) > 1) { if (substr($parts[0], 0, 3) != '___') { if (!isset($joined[$parts[0]])) { $joined[$parts[0]] = array(); } //$joined[$parts[0]][$parts[1]] = $value; $this->_recurseRecordKeys($joined[$parts[0]], $parts[1], $value); } else { $this->_recurseRecordKeys($joined, $parts[1], $value); } unset($row[$key]); } } } $row = array_merge($row, $joined); $record = new Dbi_Record($this->_model, $row); $components = $this->_model->components(); $subqueries = $components->subqueries; foreach ($subqueries as $subquery) { $cls = $subquery['model']; $m = new $cls(); $tokens = Dbi_Sql_Tokenizer::Tokenize($subquery['statement']); // TODO: This token replacement is apparently limited in that it // will only work for two levels of subqueries. I say "apparently" // because I'm not completely sure how or why it works. foreach ($tokens as &$t) { $t = str_replace("{$subquery['name']}.", $m->name() . ".", $t); $words = explode('.', $t); if (count($words) == 2 && $words[0] == $this->_model->name() && $this->_model->name() != $m->name()) { array_shift($words); $t = '?'; $subquery['args'][] = $record[$words[0]]; } else { if (count($words) == 1 && $this->_model->field($words[0])) { $t = '?'; $subquery['args'][] = $record[$words[0]]; } } } $statement = implode(' ', $tokens); $args = array_merge(array($statement), $subquery['args']); call_user_func_array(array($m, 'where'), $args); $record[$subquery['name']] = $m; } return $record; }
private function _build(Dbi_Sql_Query_Select $select, Dbi_Model $query, $components, $parent = '', $forceLeft = false) { $subs = array(); if (count($components['fields'])) { $select->field($components['fields']); } else { foreach ($query->fields() as $name => $field) { if ($parent) { $select->field('`' . substr($parent, 0, -1) . "`.`{$name}` AS `{$parent}{$name}`"); } else { $select->field("`{$components['table']}`.`{$name}` AS `{$parent}{$name}`"); } } foreach ($components['innerJoins'] as $innerJoin) { $subquery = $innerJoin['model']; $subcomponents = $subquery->components(); $subcomponents['table'] = $innerJoin['name']; $subs[] = array('query' => $subquery, 'components' => $subcomponents, 'forceLeft' => $forceLeft); } foreach ($components['leftJoins'] as $join) { $subquery = $join['model']; $subcomponents = $subquery->components(); $subcomponents['table'] = $join['name']; $subs[] = array('query' => $subquery, 'components' => $subcomponents, 'forceLeft' => true); } foreach ($components['rightJoins'] as $join) { $subquery = $join['model']; $subcomponents = $subquery->components(); $subcomponents['table'] = $join['name']; $subs[] = array('query' => $subquery, 'components' => $subcomponents, 'forceLeft' => true); } } foreach ($components['calculated'] as $calc) { $select->field($calc); } // Where criteria $fields = array_keys($query->fields()); foreach ($components['where'] as $where) { $orStatements = array(); $orParameters = array(); foreach ($where->expressions() as $or) { $statement = $or->statement(); $tokens = Dbi_Sql_Tokenizer::Tokenize($statement); foreach ($tokens as &$token) { if (in_array($token, $fields)) { $token = "{$parent}{$components['table']}.{$token}"; } } $compiled = implode(' ', $tokens); // Functions in MySql cannot have a space before the parenthesis $compiled = str_replace(' (', '(', $compiled); $orStatements[] = $compiled; $orParameters = array_merge($orParameters, $or->parameters()); } $args = array_merge(array(implode(' OR ', $orStatements)), $orParameters); call_user_func_array(array($select, 'where'), $args); } if (!$forceLeft) { foreach ($components['innerJoins'] as $join) { $args = $join['args']; array_unshift($args, $join['model']->prefix() . $join['model']->name() . ' AS `' . $parent . $join['name'] . '`'); $tokens = Dbi_Sql_Tokenizer::Tokenize($args[1]); foreach ($tokens as &$token) { if (in_array($token, $fields)) { $token = '`' . ($parent ? substr($parent, 0, -1) : $components['table']) . "`.`{$token}`"; } else { if (substr($token, 0, strlen($join['name']) + 1) == "{$join['name']}.") { $token = "`{$parent}{$join['name']}`.`" . substr($token, strlen($join['name']) + 1) . "`"; } else { //echo "token {$token} for " . get_class($join['model']) . "<br/>"; } } } $args[1] = implode(' ', $tokens); if ($forceLeft) { call_user_func_array(array($select, 'leftJoin'), $args); } else { call_user_func_array(array($select, 'innerJoin'), $args); } } } $leftJoins = $components['leftJoins']; if ($forceLeft) { $leftJoins = array_merge($leftJoins, $components['innerJoins']); } foreach ($leftJoins as $join) { $args = $join['args']; array_unshift($args, $join['model']->prefix() . $join['model']->name() . ' AS `' . $parent . $join['name'] . '`'); $tokens = Dbi_Sql_Tokenizer::Tokenize($args[1]); foreach ($tokens as &$token) { if (in_array($token, $fields)) { //$token = "`{$components['table']}`.`{$token}`"; $token = '`' . ($parent ? substr($parent, 0, -1) : $components['table']) . "`.`{$token}`"; } else { if (substr($token, 0, strlen($join['name']) + 1) == "{$join['name']}.") { $token = "`{$parent}{$join['name']}`.`" . substr($token, strlen($join['name']) + 1) . "`"; } } } $args[1] = implode(' ', $tokens); call_user_func_array(array($select, 'leftJoin'), $args); } foreach ($components['rightJoins'] as $join) { $args = $join['args']; array_unshift($args, $join['model']->prefix() . $join['model']->name() . ' AS `' . $parent . $join['name'] . '`'); $tokens = Dbi_Sql_Tokenizer::Tokenize($args[1]); foreach ($tokens as &$token) { if (in_array($token, $fields)) { //$token = "`{$components['table']}`.`{$token}`"; $token = '`' . ($parent ? substr($parent, 0, -1) : $components['table']) . "`.`{$token}`"; } else { if (substr($token, 0, strlen($join['name']) + 1) == "{$join['name']}.") { $token = "`{$parent}{$join['name']}`.`" . substr($token, strlen($join['name']) + 1) . "`"; } } } $args[1] = implode(' ', $tokens); call_user_func_array(array($select, 'rightJoin'), $args); } foreach ($components['groups'] as $group) { $select->group($group); } foreach ($components['having'] as $having) { $orStatements = array(); $orParameters = array(); foreach ($having->expressions() as $or) { $statement = $or->statement(); $tokens = Dbi_Sql_Tokenizer::Tokenize($statement); foreach ($tokens as &$token) { if (in_array($token, $fields)) { $token = "{$parent}{$components['table']}.{$token}"; } } $compiled = implode(' ', $tokens); // Functions in MySql cannot have a space before the parenthesis $compiled = str_replace(' (', '(', $compiled); $orStatements[] = $compiled; $orParameters = array_merge($orParameters, $or->parameters()); } $args = array_merge(array(implode(' OR ', $orStatements)), $orParameters); call_user_func_array(array($select, 'having'), $args); } foreach ($subs as $sub) { $this->_build($select, $sub['query'], $sub['components'], $parent . ($sub['components']['table'] ? $sub['components']['table'] . '.' : ''), $sub['forceLeft']); } }
/** * Set an array of data. This function is used internally for actions that * are allowed to modify primary keys. * @param array $data */ private function _cleanArray(array $data) { $joinModels = array(); $components = $this->_model->components(); foreach ($components['leftJoins'] as $join) { if (!empty($join['name'])) { $joinModels[$join['name']] = $join['model']; } } foreach ($components['innerJoins'] as $join) { if (!empty($join['name'])) { $joinModels[$join['name']] = $join['model']; } } foreach ($data as $key => $value) { if (isset($joinModels[$key])) { $mod = $joinModels[$key]; $value = new Dbi_Record($mod, $value); } $this->_data[$key] = $value; } foreach ($components['subqueries'] as $subquery) { $cls = $subquery['model']; $m = new $cls(); $tokens = Dbi_Sql_Tokenizer::Tokenize($subquery['statement']); // TODO: This token replacement is apparently limited in that it // will only work for two levels of subqueries. I say "apparently" // because I'm not completely sure how or why it works. foreach ($tokens as &$t) { $t = str_replace("{$subquery['name']}.", $m->name() . ".", $t); $words = explode('.', $t); if (count($words) == 2 && $words[0] == $this->_model->name() && $this->_model->name() != $m->name()) { array_shift($words); $t = '?'; $subquery['args'][] = $this->_data[$words[0]]; } else { if (count($words) == 2 && $words[0] == $m->name()) { //echo $m->name() . '.' . $record[$m->name() . "." . $words[1]] . "\n"; //array_shift($words); //$t = '?'; //$subquery['args'][] = implode('.', $words); //$record[$words[0]]; //$subquery['args'][] = $m->name() . '.' . $record[$words[1]]; //$t = $m->name() . '.' . $words[1]; } else { if (count($words) == 1 && $this->_model->field($words[0])) { $t = '?'; $subquery['args'][] = $this->_data[$words[0]]; } else { //echo "Model: " . get_class($this->_model) . "\n"; } } } } $statement = implode(' ', $tokens); $args = array_merge(array($statement), $subquery['args']); call_user_func_array(array($m, 'where'), $args); $this->_data[$subquery['name']] = $m; } }