如何使用CakePHP嵌套连接?

Dar*_*mas 6 php cakephp join model

我正在努力表现.因此,而不是使用以下SQL语法:

select *
from   tableA INNER JOIN
       tableB on tableA.id = tableB.tableA_id LEFT OUTER JOIN
       ( tableC INNER JOIN tableD on tableC.tableD_id = tableD.id)
       on tableC.tableA_id = tableA.id
Run Code Online (Sandbox Code Playgroud)

我想使用CakePHP model->find().这将让我也使用它Paginator,因为据我所知,这不适用于自定义SQL查询(除非你将一个单一的分页查询硬编码到对我来说似乎有点不灵活的模型).

到目前为止我尝试过的:

/* inside tableA_controller.php, inside an action, e.g. "view" */
$this->paginate['recursive'] = -1; # suppress model associations for now
$this->paginate['joins'] = array(
    array(
        'table' => 'tableB',
        'alias' => 'TableB',
        'type'  => 'inner',
        'conditions' => 'TableB.tableA_id = TableA.id',
    ),
    array(
        'table' => 'tableC',
        'alias' => 'TableC',
        'type'  => 'left',
        'conditions' => 'TableC.tableA_id = TableA.id',
        'joins' = array( # this would be the obvious way to do it, but doesn't work
            array(
                'table' => 'tableD',
                'alias' => 'TableD',
                'type'  => 'inner',
                'conditions' => 'TableC.tableD_id = TableD.id'
            )
        )
    )
)
Run Code Online (Sandbox Code Playgroud)

也就是说,将连接嵌套到结构中.但这不起作用(CakePHP只是忽略了嵌套'joins'元素,这是我所期望的,但很难过.

我已经看到了关于如何where使用语句构建器进行子查询(在子句中)的注释中的提示.可以在这里使用类似的技巧吗?

Dar*_*mas 3

事实证明你不能。至少上面提供的语法和 CakePHP 1.2.6 都没有。我查看了源代码(是的!开源框架!)并找到了cake/libs/model/datasources/dbo_source.php包含连接代码的文件。

这一切都从DboSource::renderStatement()对数组进行浅层遍历开始$query['joins'],通过使用 SQL 片段替换这些连接定义DboSource::buildJoinStatement($join),对参数进行一些整理(填充空白等),然后调用DboSource::renderJoinStatement创建单个连接子句的 SQL 片段。

我:这应该很容易解决!

我被告知不要编辑 中的内容cake/libs,因此我将文件复制dbo_source.php到 中app/models/datasources/进行编辑。然后我拿起斧头,将数组的浅层遍历重构$query['joins']DboSource::renderStatement()一个新方法,DboSource::buildJoinStatementArray()从而产生了这两种方法:

function buildStatement($query, $model) {
    $query = array_merge(array('offset' => null, 'joins' => array()), $query);

    # refactored (extract method) to make recursion easier
    $query['joins'] = $this->buildJoinStatementArray($query['joins']);

    return $this->renderStatement('select', array(
        'conditions' => $this->conditions($query['conditions'], true, true, $model),
        'fields' => implode(', ', $query['fields']),
        'table' => $query['table'],
        'alias' => $this->alias . $this->name($query['alias']),
        'order' => $this->order($query['order']),
        'limit' => $this->limit($query['limit'], $query['offset']),
        'joins' => implode(' ', $query['joins']),
        'group' => $this->group($query['group'])
    ));
}
/**
 * Replaces the join statement array syntax with SQL join clauses.
 */
function buildJoinStatementArray($joins) {
    if (!empty($joins)) {
        $count = count($joins);
        for ($i = 0; $i < $count; $i++) {
            if (is_array($joins[$i])) {
                $joins[$i] = $this->buildJoinStatement($joins[$i]); # $joins[$i] now contains something like "LEFT JOIN users As User on User.group_id = Group.id"
            }
        }
    }
    return $joins;
}
Run Code Online (Sandbox Code Playgroud)

一旦我有了DboSource::buildJoinStatementArray(),就该改变了DboSource::buildJoinStatement()——我所做的只是添加了一个检查$data['joins']和针对这种情况的替代渲染方法:

function buildJoinStatement($join) {
    $data = array_merge(array(
        'type' => null,
        'alias' => null,
        'table' => 'join_table',
        'conditions' => array()
    ), $join);

    if (!empty($data['alias'])) {
        $data['alias'] = $this->alias . $this->name($data['alias']);
    }
    if (!empty($data['conditions'])) {
        $data['conditions'] = trim($this->conditions($data['conditions'], true, false));
    }

    # allow for nested joins
    if (!empty($data['joins']) and is_array($data['joins'])) {
        $data['joins'] = $this->buildJoinStatementArray($data['joins']);
        return $this->renderNestedJoinStatement($data);
    }
    else
    {
        return $this->renderJoinStatement($data);
    }
}
Run Code Online (Sandbox Code Playgroud)

renderNestedJoinStatement()方法非常类似于DboSource::renderJoinStatement()

/**
 * Renders a final SQL JOIN that contains nested join statements
 *
 * @param array $data
 * @return string
 */
function renderNestedJoinStatement($data) {
    extract($data);
    $nestedJoins = implode(' ', $joins);
    return trim("{$type} JOIN ({$table} {$alias} {$nestedJoins})ON ({$conditions})");
}
Run Code Online (Sandbox Code Playgroud)