public function parseJoinsCb(&$joins, $match)
{
$chain = $match['chain'];
if (!empty($chain[0]) && ($chain[0] !== '.' && $chain[0] !== ':')) {
$chain = '.' . $chain;
// unified chain format
}
preg_match_all('~
(?(DEFINE)
(?P<word> [\\w_]*[a-z][\\w_]* )
)
(?P<del> [.:])?(?P<key> (?&word))(\\((?P<throughColumn> (?&word))\\))?
~xi', $chain, $keyMatches, PREG_SET_ORDER);
$parent = $this->tableName;
$parentAlias = preg_replace('#^(.*\\.)?(.*)$#', '$2', $this->tableName);
// join schema keyMatch and table keyMatch to schema.table keyMatch
if ($this->driver->isSupported(ISupplementalDriver::SUPPORT_SCHEMA) && count($keyMatches) > 1) {
$tables = $this->getCachedTableList();
if (!isset($tables[$keyMatches[0]['key']]) && isset($tables[$keyMatches[0]['key'] . '.' . $keyMatches[1]['key']])) {
$keyMatch = array_shift($keyMatches);
$keyMatches[0]['key'] = $keyMatch['key'] . '.' . $keyMatches[0]['key'];
$keyMatches[0]['del'] = $keyMatch['del'];
}
}
// do not make a join when referencing to the current table column - inner conditions
// check it only when not making backjoin on itself - outer condition
if ($keyMatches[0]['del'] === '.') {
if (count($keyMatches) > 1 && ($parent === $keyMatches[0]['key'] || $parentAlias === $keyMatches[0]['key'])) {
throw new Nette\InvalidArgumentException("Do not prefix table chain with origin table name '{$keyMatches[0]['key']}'. If you want to make self reference, please add alias.");
}
if ($parent === $keyMatches[0]['key']) {
return "{$parent}.{$match['column']}";
} elseif ($parentAlias === $keyMatches[0]['key']) {
return "{$parentAlias}.{$match['column']}";
}
}
$tableChain = NULL;
foreach ($keyMatches as $index => $keyMatch) {
$isLast = !isset($keyMatches[$index + 1]);
if (!$index && isset($this->aliases[$keyMatch['key']])) {
if ($keyMatch['del'] === ':') {
throw new Nette\InvalidArgumentException("You are using has many syntax with alias (':{$keyMatch['key']}'). You have to move it to alias definition.");
} else {
$previousAlias = $this->currentAlias;
$this->currentAlias = $keyMatch['key'];
$requiredJoins = [];
$query = $this->aliases[$keyMatch['key']] . '.foo';
$this->parseJoins($requiredJoins, $query);
$aliasJoin = array_pop($requiredJoins);
$joins += $requiredJoins;
list($table, , $parentAlias, $column, $primary) = $aliasJoin;
$this->currentAlias = $previousAlias;
}
} elseif ($keyMatch['del'] === ':') {
if (isset($keyMatch['throughColumn'])) {
$table = $keyMatch['key'];
$belongsTo = $this->conventions->getBelongsToReference($table, $keyMatch['throughColumn']);
if (!$belongsTo) {
throw new Nette\InvalidArgumentException("No reference found for \${$parent}->{$keyMatch['key']}.");
}
list(, $primary) = $belongsTo;
} else {
$hasMany = $this->conventions->getHasManyReference($parent, $keyMatch['key']);
if (!$hasMany) {
throw new Nette\InvalidArgumentException("No reference found for \${$parent}->related({$keyMatch['key']}).");
}
list($table, $primary) = $hasMany;
}
$column = $this->conventions->getPrimary($parent);
} else {
$belongsTo = $this->conventions->getBelongsToReference($parent, $keyMatch['key']);
if (!$belongsTo) {
throw new Nette\InvalidArgumentException("No reference found for \${$parent}->{$keyMatch['key']}.");
}
list($table, $column) = $belongsTo;
$primary = $this->conventions->getPrimary($table);
}
if ($this->currentAlias && $isLast) {
$tableAlias = $this->currentAlias;
} elseif ($parent === $table) {
$tableAlias = $parentAlias . '_ref';
} elseif ($keyMatch['key']) {
$tableAlias = $keyMatch['key'];
} else {
$tableAlias = preg_replace('#^(.*\\.)?(.*)$#', '$2', $table);
}
$tableChain .= $keyMatch[0];
if (!$isLast || !$this->currentAlias) {
$this->checkUniqueTableName($tableAlias, $tableChain);
}
$joins[$tableAlias] = [$table, $tableAlias, $parentAlias, $column, $primary];
$parent = $table;
$parentAlias = $tableAlias;
}
return $tableAlias . ".{$match['column']}";
}