2010-05-06 5 views
5

나는 행동하려고하고있다. 그래서, 대신에 다음과 같은 SQL 구문을 사용하는 :CakePHP와 조인을 중첩하는 방법은 무엇입니까?

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' 
      ) 
     ) 
    ) 
) 

. 그러나 그것은 (CakePHP를 그냥 중첩 된 'joins' 종류 내가 기대했던이었다 요소,하지만 슬픈 무시 작동하지 않습니다.

나는 문 빌더를 사용하여) where 절 (하위 쿼리 작업을 수행하는 방법에 대한 의견에서 힌트를 보았다 . 비슷한 트릭은 여기에 사용할 수 있습니다

+0

왜 중첩해야합니까? 최상위 수준에서 모든 조인을 사용하여 동일한 작업을 수행 할 수 있습니까? –

+0

그럴 수는 있지만 그 결과는 달라집니다. 중첩 된 내부 조인에서 선택 데이터를 사용하여 최상위 수준 내부 조인의 모든 결과를 원합니다. 이것을 평평하게하면, 대응하는 TableD가없는 행 (TableA 내부 조인 TableB)이 풀어집니다. –

+0

와우 꽤 복잡합니다. 아마 어떤 방법으로 몇 가지 추가 매개 변수를 추가하는 find()를 시도하고 확장 할 것입니다. –

답변

2

당신은 알 수 없습니다. 적어도 CakePHP 1.2.6에서는 제공되지 않는 문법을 사용합니다. 나는 소스를 살펴 보았고 (조인! 오픈 소스 프레임 워크를!) 조인 코드가 들어있는 cake/libs/model/datasources/dbo_source.php 파일을 찾았습니다.

그것은 모든 사람들이 일부 (등 빈칸을 채우는)를 인수로 정리 수행 후 생성 DboSource::renderJoinStatement를 호출 DboSource::buildJoinStatement($join) 통해 SQL 파편 정의에 가입 대체하는 $query['joins'] 배열의 얕은 산책을한다 DboSource::renderStatement() 시작 단일 조인 절의 SQL 단편.

me : 수정하기 쉽습니다.

가 나는 cake/libs에 물건을 편집 할 수 없습니다 말했다, 그래서 대신 내가 편집을 위해 app/models/datasources/에 파일 dbo_source.php을 복사했다.그럼 난 내 도끼를 가져다가 이러한 두 가지 방법의 결과로 새로운 방법 DboSource::buildJoinStatementArray()DboSource::renderStatement()$query['joins'] 배열의 얕은 거리를 리팩토링 :

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()을 변경하는 시간이었다 - 내가 한 모든했다

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() 꽤 유사하다 : $data['joins'] 수표 그 경우에 대한 대안 렌더링 방식을 첨가

/** 
* 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})"); 
} 
0

나는이 권리를 얻고 경우에, 당신은 (잘하면 당신의 모델) 다음과 같은 관계를 가지고 :

TableA hasMany TableB. 
TableA hasMany TableC. 

TableB belongsTo TableA. 

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

TableD hasMany TableC. (might be hasOne) 

당신이 함유 성 행동을 사용하는 경우 (나는 그것을 매우 추천하고 모든 모델이 상속받을 app_model 레벨로 설정한다.) 나는 당신이 th와 비슷한 것을 할 수 있다고 생각한다. ...입니다

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

특정 필드를 선택해야한다면, 당신은 내가 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' 
     ) 
    ) 
    ) 
) 

, 나는 중첩 테이블 컨테이너 (건의 및 TableC)의 부모 인 곳을 해본 적이 없다, 그래서 작동하지 않을 수 있습니다,하지만 그것은 아마 가치 시도이다 .

+0

이것은 실제로 왼쪽 외부 조인이하는 것이 아닙니다. 조인 (TableC TableD)이 비어 있으면 [TableC]에 null 필드가 있어야합니다. 그러나 CakePHP에 중첩 된 조인을 추가하는 것은 정말 쉽습니다. 제 대답을보십시오. –

+0

좋은 지적입니다. 다행을 발견했기 때문에 기쁩니다. 솔루션이 1.3.x 분기에 적용되는지 여부를 확인하고 유스 케이스에 대한 수정으로 제출할 수 있다면 좋을 것입니다. 많은 사람들이 유익 할 것이라고 확신합니다. – ianmjones

관련 문제