# Query lang.join

Testing Is Documentation

tests/Database/Query/JoinTest.php

# join 函数原型

join($table, $cols, ...$cond);
  • 其中 $table 和 $cols 与 《查询语言.table》中的用法一致。
  • $cond 与《查询语言.where》中的用法一致。

Uses

<?php

use Leevel\Database\Condition;
use Tests\Database\DatabaseTestCase as TestCase;

# join 基础用法

public function testBaseUse(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`test_query_subsql`.`name`,`test_query_subsql`.`value` FROM `test_query` INNER JOIN `test_query_subsql` ON `test_query_subsql`.`name` = :test_query_subsql_name",
            {
                "test_query_subsql_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->join('test_query_subsql', 'name,value', 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# join 附加条件

public function testWithCondition(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` INNER JOIN `test_query_subsql` `t` ON `t`.`name` = :t_name",
            {
                "t_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->join(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true),
            1
        )
    );
}

# join 附加条件支持数组和表达式

实质上 where 支持语法特性都支持。

public function testWithConditionSupportArrayAndExpression(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`test_query_subsql`.`name`,`test_query_subsql`.`value` FROM `test_query` INNER JOIN `test_query_subsql` ON `test_query_subsql`.`hello` = :test_query_subsql_hello AND `test_query_subsql`.`test` > `test_query_subsql`.`name`",
            {
                "test_query_subsql_hello": [
                    "world"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->join('test_query_subsql', 'name,value', ['hello' => 'world', ['test', '>', Condition::raw('[name]')]])
                ->findAll(true),
            2
        )
    );
}

# join 附加条件支持闭包

public function testWithConditionIsClosure(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`test_query_subsql`.`name`,`test_query_subsql`.`value` FROM `test_query` INNER JOIN `test_query_subsql` ON (`test_query_subsql`.`id` < :test_query_subsql_id AND `test_query_subsql`.`name` LIKE :test_query_subsql_name)",
            {
                "test_query_subsql_id": [
                    5
                ],
                "test_query_subsql_name": [
                    "hello"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->join('test_query_subsql', 'name,value', function ($select) {
                    $select
                        ->where('id', '<', 5)
                        ->where('name', 'like', 'hello');
                })
                ->findAll(true),
            3
        )
    );
}

# innerJoin 查询

public function testInnerJoin(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` INNER JOIN `test_query_subsql` `t` ON `t`.`name` = :t_name",
            {
                "t_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# leftJoin 查询

public function testLeftJoin(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` LEFT JOIN `test_query_subsql` `t` ON `t`.`name` = :t_name",
            {
                "t_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->leftJoin(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# rightJoin 查询

public function testRightJoin(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` RIGHT JOIN `test_query_subsql` `t` ON `t`.`name` = :t_name",
            {
                "t_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->rightJoin(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# fullJoin 查询

public function testFullJoin(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` FULL JOIN `test_query_subsql` `t` ON `t`.`name` = :t_name",
            {
                "t_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->fullJoin(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

TIP

MySQL 不支持 FULL JOIN,仅示例。

# crossJoin 查询

自然连接不用设置 on 条件。

public function testCrossJoin(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` CROSS JOIN `test_query_subsql` `t`",
            [],
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->crossJoin(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'])
                ->findAll(true)
        )
    );
}

# naturalJoin 查询

自然连接不用设置 on 条件。

public function testNaturalJoin(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`t`.`name` AS `nikename`,`t`.`value` AS `tt` FROM `test_query` NATURAL JOIN `test_query_subsql` `t`",
            [],
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->naturalJoin(['t' => 'test_query_subsql'], ['name as nikename', 'tt' => 'value'])
                ->findAll(true)
        )
    );
}

# join 查询支持表支持查询对象

public function testInnerJoinWithTableIsSelect(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`b`.`name` AS `nikename`,`b`.`value` AS `tt` FROM `test_query` INNER JOIN (SELECT `b`.* FROM `test_query_subsql` `b`) b ON `b`.`name` = :b_name",
            {
                "b_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $joinTable = $connect->table('test_query_subsql as b');

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin($joinTable, ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# join 查询支持表支持查询条件对象

public function testInnerJoinWithTableIsCondition(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`b`.`name` AS `nikename`,`b`.`value` AS `tt` FROM `test_query` INNER JOIN (SELECT `b`.* FROM `test_query_subsql` `b`) b ON `b`.`name` = :b_name",
            {
                "b_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $joinTable = $connect
        ->table('test_query_subsql as b')
        ->databaseCondition();

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin($joinTable, ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# join 查询支持表支持闭包

public function testInnerJoinWithTableIsClosure(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`b`.`name` AS `nikename`,`b`.`value` AS `tt` FROM `test_query` INNER JOIN (SELECT `b`.* FROM `test_query_subsql` `b`) b ON `b`.`name` = :b_name",
            {
                "b_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin(function ($select) {
                    $select->table('test_query_subsql as b');
                }, ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# join 查询支持表支持数组别名

public function testInnerJoinWithTableIsArrayCondition(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`foo`.`name` AS `nikename`,`foo`.`value` AS `tt` FROM `test_query` INNER JOIN (SELECT `b`.* FROM `test_query_subsql` `b`) foo ON `foo`.`name` = :foo_name",
            {
                "foo_name": [
                    "小牛"
                ]
            },
            false
        ]
        eot;

    $joinTable = $connect
        ->table('test_query_subsql as b')
        ->databaseCondition();

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin(['foo' => $joinTable], ['name as nikename', 'tt' => 'value'], 'name', '=', '小牛')
                ->findAll(true)
        )
    );
}

# join 查询支持表支持表达式

public function testInnerJsonWithTableNameIsExpression(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`a`.`name` AS `nikename`,`a`.`value` AS `tt` FROM `test_query` INNER JOIN (SELECT * FROM test_query_subsql) a ON `a`.`name` = `test_query`.`name`",
            [],
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin('(SELECT * FROM test_query_subsql)', ['name as nikename', 'tt' => 'value'], 'name', '=', Condition::raw('[test_query.name]'))
                ->findAll(true)
        )
    );
}

# join 查询支持表支持表达式别名

public function testInnerJsonWithTableNameIsExpressionWithAsCustomAlias(): void
{
    $connect = $this->createDatabaseConnectMock();

    $sql = <<<'eot'
        [
            "SELECT `test_query`.*,`bar`.`name` AS `nikename`,`bar`.`value` AS `tt` FROM `test_query` INNER JOIN (SELECT * FROM test_query_subsql) bar ON `bar`.`name` = `test_query`.`name`",
            [],
            false
        ]
        eot;

    $this->assertSame(
        $sql,
        $this->varJson(
            $connect
                ->table('test_query')
                ->innerJoin('(SELECT * FROM test_query_subsql) as bar', ['name as nikename', 'tt' => 'value'], 'name', '=', Condition::raw('[test_query.name]'))
                ->findAll(true)
        )
    );
}