代码之家  ›  专栏  ›  技术社区  ›  Daren Thomas

如何用CakePHP嵌套连接?

  •  6
  • Daren Thomas  · 技术社区  · 16 年前

    我在努力表现。因此,不要使用以下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
    

    我想用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'
                )
            )
        )
    )
    

    'joins' 这是我所期待的,但很悲伤。

    我在评论中看到了关于如何进行子查询的提示(在 where 子句)使用语句生成器。这里能用类似的伎俩吗?

    2 回复  |  直到 16 年前
        1
  •  2
  •   Daren Thomas    16 年前

    原来你不能。至少不能使用上面提供的语法,也不能使用cakephp1.2.6。我检查了来源(耶!到开源框架!)找到了文件 cake/libs/model/datasources/dbo_source.php 它包含连接的代码。

    DboSource::renderStatement() 哪条路走的很浅 $query['joins'] 数组,通过 DboSource::buildJoinStatement($join) ,它对参数进行整理(填空等),然后调用 DboSource::renderJoinStatement 创建单个join子句的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;
    }
    

    曾经我有 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);
        }
    }
    

    新的 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})");
    }
    
        2
  •  1
  •   ianmjones    16 年前

    TableA hasMany TableB.
    TableA hasMany TableC.
    
    TableB belongsTo TableA.
    
    TableC belongsTo TableA.
    TableC belongsTo TableD. (might be hasOne)
    
    TableD hasMany TableC. (might be hasOne)
    

    $this->TableA->find(
      'all',
      array(
        'contain' => array(
          'TableB',
          'TableC' => array(
            'TableD'
          )
        ),
        'conditions' => array(...),
        'order' => array(...)
      )
    );
    

    如果需要选取特定字段,则需要在contain参数中指定它们,例如,这里我限制TableB的返回字段:

    $this->TableA->find(
      'all',
      array(
        'contain' => array(
          'TableB' => array(
            'fields' => array(
              'field_1',
              'field_2'
            ),
          ),
          'TableC' => array(
            'TableD'
          )
        ),
        'conditions' => array(...),
        'order' => array(...)
      )
    );
    

    返回的数据应该是这样的:

      [0] => array(
        [TableA] => array(
          [id] => 12,
          [name] => 'Foo'
        ),
        [TableB] => array(
          [id] => 23,
          [table_a_id] => 12,
          [name] => 'Bah'
        ),
        [TableC] => array(
          [id] => 45,
          [table_a_id] => 12,
          [table_d_id] => 67,
          [name] => 'Woo',
          [TableD] => array(
            [0] => array(
              [id] => 67,
              [table_a_id] => 12,
              [name] => 'Wah'
            )
          )
        )
      )
    

    但是,我从来没有在嵌套表是容器(TableD和TableC)的父级的情况下这样做过,所以它可能不起作用,但可能值得一试。

    推荐文章