@param Expression $foreign
@param Where      $on
@param string     $joinType

@return $this
968068
public function add(Expression $foreign, Where $on, $joinType = self::INNER)
    {
        $this->joins->add(new Join($this->getBuilder(), $foreign, $on, $joinType));
        
        return $this;
    }
154282
protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
    {
        $this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');

        if (! $value instanceof Expression) {
            $this->addBinding($value, 'where');
        }

        return $this;
    }
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;
    }
401570
public function join(string $table, string $column, string $toColumn, string $type = Query\JoinType::LEFT)
    {
        $alias = $this->joinCurrentAlias++;

        $toAlias = self::cutAlias($toColumn);

        $this->joins[$alias] = [
            'type'   => $type,
            'table'  => $table,
            'column' => $column,
            'to'     => [$toAlias, $toColumn],
        ];

        return $this;
    }
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 .= "
        }
    }
";
    }
765591
public static function relation($local, $foreign)
    {
        $relations = Config::table($local)->relations();
        if (isset($relations->{$foreign}))
        {
            return TRUE;
        }

        throw new LazerException('Relation "' . $local . '" to "' . $foreign . '" doesn\'t exist');
    }
1297525
public function leftJoin($join, $on = null)
    {
        $this->getJoins()->leftJoin($join, $on);

        return $this;
    }
534126
private function createJoin(string $alias, string $field, string $joinType): string
    {
        $relationship = $alias . '.' . $field;
        $key = $relationship . '.' . $joinType;

        if (!isset($this->uniqueJoins[$key])) {
            $joinedAlias = $this->uniqueNameFactory->createAliasName($field);

            if ($joinType === 'innerJoin') {
                $this->queryBuilder->innerJoin($relationship, $joinedAlias);
            } else {
                $this->queryBuilder->leftJoin($relationship, $joinedAlias);
            }

            // TODO: For now we assume the query will always access some field on the relation, so we optimize SQL by
            // fetching those objects in a single SQL query. But this should be revisited by either exposing an option
            // to the API so the client could decide to select or not the relations, or even better to detect in GraphQL
            // query if it's actually used or not.
            $this->queryBuilder->addSelect($joinedAlias);

            $this->uniqueJoins[$key] = $joinedAlias;
        }

        return $this->uniqueJoins[$key];
    }
1363513
public function onRelationalExpression($expressionType, $left, $right)
    {
        $type = $this->unpackExpressionType($expressionType);
        switch ($type) {
            case ExpressionType::GREATERTHAN:
                return $this->prepareBinaryExpression(self::GREATER_THAN, $left, $right);
            case ExpressionType::GREATERTHAN_OR_EQUAL:
                return $this->prepareBinaryExpression(self::GREATER_THAN_OR_EQUAL, $left, $right);
            case ExpressionType::LESSTHAN:
                return $this->prepareBinaryExpression(self::LESS_THAN, $left, $right);
            case ExpressionType::LESSTHAN_OR_EQUAL:
                return $this->prepareBinaryExpression(self::LESS_THAN_OR_EQUAL, $left, $right);
            case ExpressionType::EQUAL:
                return $this->prepareBinaryExpression(self::EQUAL, $left, $right);
            case ExpressionType::NOTEQUAL:
                return $this->prepareBinaryExpression(self::NOT_EQUAL, $left, $right);
            default:
                throw new \InvalidArgumentException('onRelationalExpression');
        }
    }
1115792
private function addJoin($joinType)
    {
        $from = $this->getChildOfType(QBConstants::NT_FROM);
        $curSource = $from->getChild(QBConstants::NT_SOURCE);

        $src = new SourceJoin($this, $joinType);
        $src->left()->addChild($curSource);
        $from->setChild($src);

        return $src;
    }
751897
protected function join(
        $type,
        $joinTableName,
        $key,
        $comparator,
        $value = null,
        $shouldCompareFields = true,
        $tableName = null
    ) {
        if (!$tableName) {
            $tableName = $this->getTableName();
        }

        $condition = new \Parable\ORM\Query\Condition();
        $condition
            ->setQuery($this)
            ->setTableName($tableName)
            ->setJoinTableName($joinTableName)
            ->setKey($key)
            ->setComparator($comparator)
            ->setValue($value)
            ->setShouldCompareFields($shouldCompareFields);

        $this->joins[$type][] = $condition;
        return $this;
    }
1339308
public function addOneToMany($schema, $foreign_id = null, $col = null, $name = null, $one = null)
  {
    $m = $this->getManager();
    $s = $schema instanceof Schema ? $schema : $m->getSchema($schema, true);
    $n = $name ?: $s->getName();

    $this->relations[$n] = array(
      'type'       => static::RELATION_ONE_TO_MANY,
      'column'     => $col ?: $this->getPrimaryKey(),
      'schema'     => $s,
      'foreign_id' => $foreign_id ?: StaticStringy::underscored($this->getItemClass(false) . '_id'),
      'name'       => $n,
      'one'        => $one ?: $s->getItemClass(false),
    );

    return $this;
  }
817710
public function on($column1, $column2 = null, $operator = '=')
    {
        return $this->addJoinCondition($column1, $column2, $operator);
    }
905591
public function sqlJoin($joinType, $tableName, $on, $alias)
    {
        if (is_string($tableName)) {
            $tableName = TableNameResolver::getTableName($tableName, $this->tablePrefix);
        } elseif ($tableName instanceof self) {
            $tableName = $tableName->toSQL();
        }

        $onSQL = [];
        if (is_string($on)) {
            $onSQL[] = $this->getAdapter()->quoteSql($on);
        } else {
            foreach ($on as $leftColumn => $rightColumn) {
                if ($rightColumn instanceof Expression) {
                    $onSQL[] = $this->getQuotedName($leftColumn).'='.$this->getAdapter()->quoteSql($rightColumn->toSQL());
                } else {
                    $onSQL[] = $this->getQuotedName($leftColumn).'='.$this->getQuotedName($rightColumn);
                }
            }
        }

        if (false !== strpos($tableName, 'SELECT')) {
            return $joinType.' ('.$this->getAdapter()->quoteSql($tableName).')'.(empty($alias) ? '' : ' AS '.$this->getQuotedName($alias)).' ON '.implode(',', $onSQL);
        }

        return $joinType.' '.$this->getQuotedName($tableName).(empty($alias) ? '' : ' AS '.$this->getQuotedName($alias)).' ON '.implode(',', $onSQL);
    }
1061162
public function join($tableName, $joinType = 'left', $on, $joinTableFields = [])
    {
        switch (strtolower($joinType)) {
            case 'left':
                $joinType = ' LEFT JOIN ';
                break;
            case 'right':
                $joinType = ' RIGHT JOIN ';
                break;
            case 'inner':
                $joinType = ' INNER JOIN ';
                break;
            default:
                $joinType = ' LEFT JOIN ';
                break;
        }

        if (!empty($joinTableFields)) {
            $this->fields = array_merge($this->fields, $joinTableFields);
        }

        $this->setJoins($tableName, $joinType . $tableName . ' ON ' . $on);

        return $this;
    }
683642
public function getJoinAnnotation($joinType, $entity, $mappedBy = null, $inversedBy = null)
    {
        return $this->getAnnotation($joinType, array('targetEntity' => $entity, 'mappedBy' => $mappedBy, 'inversedBy' => $inversedBy));
    }
1191915
public function join($table, $on, $type = 'full', $alias = '')
    {
        if (!isset($this->joins)) {
            $this->joins = [];
        }
        $join = [
            'table' => $this->trimName($table),
            'on' => $on,
            'type' => $type,
        ];
        if (!empty($alias)) {
            $join['alias'] = $this->trimName($alias);
        }
        $this->joins = array_merge($this->joins, [$join]);
        return $this;
    }