@param string|array|Expression $foreign
@param array|Statement\Where   $on
@param string                  $joinType

@return $this
11351
public function join($foreign, $on, $joinType = Joins::INNER)
    {
        $builder = $this->getJoinStatement()->getBuilder();
        $foreign = is_array($foreign) ? $foreign : [$foreign];
        $foreign[0] = !($foreign[0] instanceof Expression) ? new Table($foreign[0]) : $foreign[0];
        
        $foreign = $builder->completeExpression(...$foreign);
        
        if (is_array($on)) {
            $whereOn = new Statement\Where($builder);
            $on = count($on) === count($on, true) ? [$on] : $on;
            
            foreach ($on as $criteria) {
                list($criteria[0], $criteria[1]) = [
                    ($criteria[0] instanceof Expression) ? $criteria[0] : $builder->createColumn($criteria[0]),
                    ($criteria[1] instanceof Expression) ? $criteria[1] : $builder->createColumn($criteria[1]),
                ];
                $whereOn->where(...$criteria);
            }
            
            $on = $whereOn;
        }
        
        $this->addJoin($foreign, $on, $joinType);
        
        return $this;
    }
701685
protected function addFKAccessor(&$script, ForeignKey $fk)
    {
        $table = $this->getTable();

        $varName = $this->getFKVarName($fk);

        $fkPeerBuilder = $this->getNewPeerBuilder($this->getForeignTable($fk));
        $fkObjectBuilder = $this->getNewObjectBuilder($this->getForeignTable($fk))->getStubObjectBuilder();
        $className = $fkObjectBuilder->getClassname(); // get the Classname that has maybe a prefix

        $and = "";
        $conditional = "";
        $localColumns = array(); // foreign key local attributes names
        $argmap = array(); // foreign -> local mapping

        // If the related columns are a primary key on the foreign table
        // then use retrieveByPk() instead of doSelect() to take advantage
        // of instance pooling
        $useRetrieveByPk = $fk->isForeignPrimaryKey();

        foreach ($fk->getLocalColumns() as $columnName) {

            $lfmap = $fk->getLocalForeignMapping();

            $localColumn = $table->getColumn($columnName);
            $foreignColumn = $fk->getForeignTable()->getColumn($lfmap[$columnName]);

            $column = $table->getColumn($columnName);
            $cptype = $column->getPhpType();
            $clo = strtolower($column->getName());
            $localColumns[$foreignColumn->getPosition()] = '$this->' . $clo;

            if ($cptype == "integer" || $cptype == "float" || $cptype == "double") {
                $conditional .= $and . "\$this->" . $clo . " != 0";
            } elseif ($cptype == "string") {
                $conditional .= $and . "(\$this->" . $clo . " !== \"\" && \$this->" . $clo . " !== null)";
            } else {
                $conditional .= $and . "\$this->" . $clo . " !== null";
            }

            $argmap[] = array('foreign' => $foreignColumn, 'local' => $localColumn);
            $and = " && ";
        }

        ksort($localColumns); // restoring the order of the foreign PK
        $localColumns = count($localColumns) > 1 ? ('array(' . implode(', ', $localColumns) . ')') : reset($localColumns);

        // If the related column is a primary kay and if it's a simple association,
        // The use retrieveByPk() instead of doSelect() to take advantage of instance pooling
        $useRetrieveByPk = count($argmap) == 1 && $argmap[0]['foreign']->isPrimaryKey();

        $script .= "

    /**
     * Get the associated $className object
     *
     * @param      PropelPDO Optional Connection object.
     * @return                 $className The associated $className object.
     * @throws PropelException
     */
    public function get" . $this->getFKPhpNameAffix($fk, $plural = false) . "(PropelPDO \$con = null)
    {";
        $script .= "
        if (\$this->$varName === null && ($conditional)) {";
        if ($useRetrieveByPk) {
            $script .= "
            \$this->$varName = " . $fkPeerBuilder->getPeerClassname() . "::retrieveByPk($localColumns, \$con);";
        } else {
            $script .= "
            \$c = new Criteria(" . $fkPeerBuilder->getPeerClassname() . "::DATABASE_NAME);";
            foreach ($argmap as $el) {
                $fcol = $el['foreign'];
                $lcol = $el['local'];
                $clo = strtolower($lcol->getName());
                $script .= "
            \$c->add(" . $fkPeerBuilder->getColumnConstant($fcol) . ", \$this->" . $clo . ");";
            }
            $script .= "
            \$this->$varName = " . $fkPeerBuilder->getPeerClassname() . "::doSelectOne(\$c, \$con);";
        }
        if ($fk->isLocalPrimaryKey()) {
            $script .= "
            // Because this foreign key represents a one-to-one relationship, we will create a bi-directional association.
            \$this->{$varName}->set" . $this->getRefFKPhpNameAffix($fk, $plural = false) . "(\$this);";
        } else {
            $script .= "
            /* The following can be used additionally to
               guarantee the related object contains a reference
               to this object.  This level of coupling may, however, be
               undesirable since it could result in an only partially populated collection
               in the referenced object.
               \$this->{$varName}->add" . $this->getRefFKPhpNameAffix($fk, $plural = true) . "(\$this);
             */";
        }

        $script .= "
        }

        return \$this->$varName;
    }
";
    }
234718
public function crossJoin($table, $on, $joinField = null)
    {
        return $this->join($table, $on, $joinField, 'CROSS');
    }
1265109
public function join($table, $on = null, $where = null, $binds = [], $type = 'LEFT')
    {
        if ($table instanceof Relation) {
            if (is_only_callable($on)) {
                /**
                 * Is this needed?
                 */
                Reflect::call($on, [$table, $table->getQuery()]);
            }

            $table->mergeToQuery($this->getQuery());
        } elseif ($table instanceof Entity) {
            $query = $table->getQuery();

            $sql = $query->buildSQL();

            $this->getQuery()->join(
                $type . ' JOIN (' . $sql . ') AS `' . $where . '` ON (' .
                (strpos($on, '(') ? '' : ($where . '.')) . $on . ')',
                null,
                null,
                $query->buildBinds()
            );
        } else {
            $this->getQuery()->join($table, $on, $where, $binds);
        }

        return $this;
    }
167040
public function join(string $table, array $on, string $type = "INNER")
    {
        if (count($on) > 2) {
            throw new PhiberException("error_on_join");
        }

        try {
            switch (strtoupper($type)) {
                case "INNER":
                    return ["join" => "INNER JOIN " . $table . " ON " . $on[0] . " = " . $on[1]];
                    break;

                case "LEFT":
                    return ["join" => "LEFT JOIN " . $table . " ON " . $on[0] . " = " . $on[1]];
                    break;

                case "RIGHT":
                    return ["join" => "RIGHT JOIN " . $table . " ON " . $on[0] . " = " . $on[1]];
                    break;

                case "FULL OUTER":
                    return ["join" => "FULL OUTER JOIN " . $table . " ON " . $on[0] . " = " . $on[1]];
                    break;

                default:
                    return ["join" => "INNER JOIN " . $table . " ON " . $on[0] . " = " . $on[1]];
            }

        } catch (PhiberException $phiberException) {
            throw new PhiberException("join_no_exists");
        }
    }
1157633
public function addJoin($key, array $parameters, $joinType = null)
    {
        if ( ! is_null($joinType)) {

            if ($joinType == 'join' || stripos($joinType, 'inner') !== false) {
                $this->joinTypes[$key] = 'join';
            } elseif (stripos($joinType, 'right') !== false) {
                $this->joinTypes[$key] = 'rightJoin';
            } else {
                unset( $this->joinTypes[$key] );
            }
        }

        $this->joins[$key] = $parameters;

        return $this;
    }
797477
public function withJoin($with, $joinType = '')
    {
        if (empty($with)) {
            return $this;
        }

        if (is_string($with)) {
            $with = explode(',', $with);
        }

        $first = true;

        /** @var Model $class */
        $class = $this->model;
        foreach ($with as $key => $relation) {
            $closure = null;
            $field   = true;

            if ($relation instanceof \Closure) {
                // 支持闭包查询过滤关联条件
                $closure  = $relation;
                $relation = $key;
            } elseif (is_array($relation)) {
                $field    = $relation;
                $relation = $key;
            } elseif (is_string($relation) && strpos($relation, '.')) {
                list($relation, $subRelation) = explode('.', $relation, 2);
            }

            /** @var Relation $model */
            $relation = Db::parseName($relation, 1, false);
            $model    = $class->$relation();

            if ($model instanceof OneToOne) {
                $model->eagerly($this, $relation, $field, $joinType, $closure, $first);
                $first = false;
            } else {
                // 不支持其它关联
                unset($with[$key]);
            }
        }

        $this->via();

        $this->options['with_join'] = $with;

        return $this;
    }
392845
protected function addFilterByFk(&$script, $fk)
    {
        $this->declareClasses('PropelObjectCollection', 'PropelCollection', 'PropelException');
        $table = $this->getTable();
        $queryClass = $this->getStubQueryBuilder()->getClassname();
        $fkTable = $this->getForeignTable($fk);
        $fkStubObjectBuilder = $this->getNewStubObjectBuilder($fkTable);
        $this->declareClassFromBuilder($fkStubObjectBuilder);
        $fkPhpName = $fkStubObjectBuilder->getClassname();
        $relationName = $this->getFKPhpNameAffix($fk);
        $objectName = '$' . $fkTable->getStudlyPhpName();
        $script .= "
    /**
     * Filter the query by a related $fkPhpName object
     *";
        if ($fk->isComposite()) {
            $script .= "
     * @param   $fkPhpName $objectName The related object to use as filter";
        } else {
            $script .= "
     * @param   $fkPhpName|PropelObjectCollection $objectName The related object(s) to use as filter";
        }
        $script .= "
     * @param     string \$comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
     *
     * @return                 $queryClass The current query, for fluid interface
     * @throws PropelException - if the provided filter is invalid.
     */
    public function filterBy$relationName($objectName, \$comparison = null)
    {
        if ($objectName instanceof $fkPhpName) {
            return \$this";
        foreach ($fk->getLocalForeignMapping() as $localColumn => $foreignColumn) {
            $localColumnObject = $table->getColumn($localColumn);
            $foreignColumnObject = $fkTable->getColumn($foreignColumn);
            $script .= "
                ->addUsingAlias(" . $this->getColumnConstant($localColumnObject) . ", " . $objectName . "->get" . $foreignColumnObject->getPhpName() . "(), \$comparison)";
        }
        $script .= ";";
        if (!$fk->isComposite()) {
            $localColumnConstant = $this->getColumnConstant($fk->getLocalColumn());
            $foreignColumnName = $fk->getForeignColumn()->getPhpName();
            $keyColumn = $fk->getForeignTable()->hasCompositePrimaryKey() ? $foreignColumnName : 'PrimaryKey';
            $script .= "
        } elseif ($objectName instanceof PropelObjectCollection) {
            if (null === \$comparison) {
                \$comparison = Criteria::IN;
            }

            return \$this
                ->addUsingAlias($localColumnConstant, {$objectName}->toKeyValue('$keyColumn', '$foreignColumnName'), \$comparison);";
        }
        $script .= "
        } else {";
        if ($fk->isComposite()) {
            $script .= "
            throw new PropelException('filterBy$relationName() only accepts arguments of type $fkPhpName');";
        } else {
            $script .= "
            throw new PropelException('filterBy$relationName() only accepts arguments of type $fkPhpName or PropelCollection');";
        }
        $script .= "
        }
    }
";
    }
467458
public function join($table, $on = null, $using = null, $type = 'JOIN')
    {
        $this->from->addJoin($table, $on, $using, $type);

        return $this;
    }
598486
public function join($joinType, $alias, $table, $condition = null, $prefix = null)
    {
        $this->getQueryHelper()->addTable($alias, $table, $condition, $joinType, $prefix);

        return $this;
    }
975356
public static function process_foreign_keys($foreign_keys, $db = null)
	{
		if ( ! is_array($foreign_keys))
		{
			throw new \Database_Exception('Foreign keys on create_table() must be specified as an array');
		}

		$fk_list = array();

		foreach($foreign_keys as $definition)
		{
			// some sanity checks
			if (empty($definition['key']))
			{
				throw new \Database_Exception('Foreign keys on create_table() must specify a foreign key name');
			}
			if ( empty($definition['reference']))
			{
				throw new \Database_Exception('Foreign keys on create_table() must specify a foreign key reference');
			}
			if (empty($definition['reference']['table']) or empty($definition['reference']['column']))
			{
				throw new \Database_Exception('Foreign keys on create_table() must specify a reference table and column name');
			}

			$sql = '';
			! empty($definition['constraint']) and $sql .= " CONSTRAINT ".\DB::quote_identifier($definition['constraint']);
			$sql .= " FOREIGN KEY (".\DB::quote_identifier($definition['key']).')';
			$sql .= " REFERENCES ".\DB::quote_identifier(\DB::table_prefix($definition['reference']['table'], static::$connection)).' (';
			if (is_array($definition['reference']['column']))
			{
				$sql .= implode(', ', \DB::quote_identifier($definition['reference']['column']));
			}
			else
			{
				$sql .= \DB::quote_identifier($definition['reference']['column']);
			}
			$sql .= ')';
			! empty($definition['on_update']) and $sql .= " ON UPDATE ".$definition['on_update'];
			! empty($definition['on_delete']) and $sql .= " ON DELETE ".$definition['on_delete'];

			$fk_list[] = "\n\t".ltrim($sql);
		}

		return ', '.implode(',', $fk_list);
	}
115845
protected function addUseRefFkQuery(&$script, ForeignKey $fk)
    {
        $fkTable = $this->getTable()->getDatabase()->getTable($fk->getTableName());
        $fkQueryBuilder = $this->getNewStubQueryBuilder($fkTable);
        $queryClass = $this->getClassNameFromBuilder($fkQueryBuilder, true);
        $relationName = $this->getRefFKPhpNameAffix($fk);
        $joinType = $this->getJoinType($fk);

        $this->addUseRelatedQuery($script, $fkTable, $queryClass, $relationName, $joinType);
    }
1303344
public function join($table, $on = null, $where = null, $binds = [])
    {
        if (!$on) {
            $this->join[] = $table;
        } else {
            $this->join[] = $table . (strpos($table, ' ON ') ? ' AND ' : ' ON ') . $on;
        }

        if ($where) {
            $this->where(new Raw($where));
        }

        if ($binds) {
            foreach ($binds as $bind) {
                $this->bind($bind, 'join');
            }
        }

        return $this;
    }
429392
function of(Row $foreign, $ref = null, $order = null, $limit = 0, $offset = 0) {
		// select * from $this where $this->$foreignColumn = $foreign->$referencedColumn
		
		if (!($rel = $this->getRelation($foreign->getTable()->getName(), $ref))) {
			return $this->onResult(null);
		}
		
		$where = array();
		foreach ($rel as $key => $ref) {
			$where["$key="] = $foreign->$ref;
		}
		
		return $this->find($where, $order, $limit, $offset);
	}
826601
public function addJoin(string $tableKey, string $type, ?string $source, $table, ?string $alias, $on = null, array $params = [])
    {
        $this->join[$tableKey] = [
            'type'   => $type,
            'source' => $source,

            'table' => $table,
            'alias' => $alias,

            'on'     => $on,
            'params' => $params,
        ];

        return $this;
    }
1256342
public function doJoin($joinType, $table, $alias = null, $joinOn = null)
    {
        $this->joinTables[] = $tableObject = $this->createTable($table, $alias);

        $joinMetadata = new JoinMetadata();
        $joinMetadata->setJoinType($joinType);

        if ($joinOn) {
            $relativeOn = new RelativeClause($tableObject, $joinOn, RelativeClause::SELF_DIRECTIVE);
            $joinMetadata->setJoinOn($relativeOn);
        }

        $tableObject->setJoinMetadata($joinMetadata);
        is_callable($table) && $table($tableObject, $joinMetadata);

        return $tableObject;
    }
399241
protected function addFilterByRefFk(&$script, ForeignKey $fk)
    {
        $this->declareClasses(
            '\Propel\Runtime\Collection\ObjectCollection',
            '\Propel\Runtime\Exception\PropelException'
        );
        $queryClass = $this->getQueryClassName();
        $fkTable = $this->getTable()->getDatabase()->getTable($fk->getTableName());
        $fkStubObjectBuilder = $this->getNewStubObjectBuilder($fkTable);
        $this->declareClassFromBuilder($fkStubObjectBuilder);
        $fkPhpName = $this->getClassNameFromBuilder($fkStubObjectBuilder, true);
        $relationName = $this->getRefFKPhpNameAffix($fk);
        $objectName = '$' . $fkTable->getCamelCaseName();
        $script .= "
    /**
     * Filter the query by a related $fkPhpName object
     *
     * @param $fkPhpName|ObjectCollection $objectName the related object to use as filter
     * @param string \$comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
     *
     * @return $queryClass The current query, for fluid interface
     */
    public function filterBy$relationName($objectName, \$comparison = null)
    {
        if ($objectName instanceof $fkPhpName) {
            return \$this";
        foreach ($fk->getInverseMapping() as $mapping) {
            /** @var Column $foreignColumn */
            list($localValueOrColumn, $foreignColumn) = $mapping;
            $rightValue = "{$objectName}->get" . $foreignColumn->getPhpName() . "()";

            if ($localValueOrColumn instanceof Column) {
                $script .= "
                ->addUsingAlias(" . $this->getColumnConstant($localValueOrColumn) . ", $rightValue, \$comparison)";
            } else {
                $leftValue = var_export($localValueOrColumn, true);
                $bindingType = $foreignColumn->getPDOType();
                $script .= "
                ->where(\"$leftValue = ?\", $rightValue, $bindingType)";
            }

        }
        $script .= ";";
        if (!$fk->isComposite()) {
            $script .= "
        } elseif ($objectName instanceof ObjectCollection) {
            return \$this
                ->use{$relationName}Query()
                ->filterByPrimaryKeys({$objectName}->getPrimaryKeys())
                ->endUse();";
        }
        $script .= "
        } else {";
        if ($fk->isComposite()) {
            $script .= "
            throw new PropelException('filterBy$relationName() only accepts arguments of type $fkPhpName');";
        } else {
            $script .= "
            throw new PropelException('filterBy$relationName() only accepts arguments of type $fkPhpName or Collection');";
        }
        $script .= "
        }
    }
";
    }