2010-05-06 35 views
5

Tôi đang cố gắng hành xử. Vì vậy, thay vì sử dụng cú pháp SQL sau:Làm thế nào để lồng ghép với 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 

Tôi muốn sử dụng CakePHP model->find(). Điều này cũng sẽ cho phép tôi sử dụng Paginator, vì điều đó sẽ không hoạt động với các truy vấn SQL tùy chỉnh theo như tôi hiểu (trừ khi bạn mã hóa một truy vấn phân trang đơn lẻ thành mô hình có vẻ không phù hợp với tôi).

Những gì tôi đã cố gắng cho đến nay:

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

Đó là, làm tổ trong tham gia vào cấu trúc. Nhưng điều đó không hiệu quả (CakePHP chỉ bỏ qua phần tử lồng nhau là 'joins' mà là loại mà tôi mong đợi, nhưng thật buồn.

Tôi đã thấy các gợi ý về cách thực hiện truy vấn phụ (trong mệnh đề where) bằng cách sử dụng trình tạo câu lệnh . một thủ thuật tương tự có thể được sử dụng ở đây

+0

Tại sao phải lồng nhau? Bạn không thể làm điều tương tự với tất cả các gia nhập ở cấp cao nhất? –

+0

Tôi ước tôi có thể, nhưng kết quả là khác nhau: Tôi muốn tất cả các kết quả từ việc kết nối bên trong cấp cao nhất với dữ liệu tùy chọn được gắn vào từ kết nối bên trong lồng nhau. Nếu tôi flatten này, sau đó tôi mất bất kỳ hàng trong (TableA bên trong tham gia TableB) mà không có TableD tương ứng ... –

+0

Wow đó là khá phức tạp. Tôi có lẽ sẽ cố gắng và mở rộng find() bằng cách nào đó để thêm một số tham số thêm –

Trả lời

2

Hóa ra bạn không thể. Ít nhất không phải với cú pháp được cung cấp ở trên và không phải với CakePHP 1.2.6. Tôi đã đi qua nguồn (yay! Để mở các khung nguồn!) Và tìm thấy tệp cake/libs/model/datasources/dbo_source.php chứa mã cho các kết nối.

Tất cả bắt đầu với DboSource::renderStatement() mà không đi dạo cạn của mảng $query['joins'], thay thế những định nghĩa tham gia với mảnh SQL qua DboSource::buildJoinStatement($join), mà hiện một số làm sạch của các đối số (điền vào chỗ trống vv) và sau đó gọi DboSource::renderJoinStatement tạo đoạn SQL của một mệnh đề nối đơn.

tôi: Điều đó phải dễ sửa!

Tôi đã nói không phải chỉnh sửa nội dung trong cake/libs, vì vậy thay vào đó tôi sao chép các tập tin dbo_source.php-app/models/datasources/ để chỉnh sửa.Sau đó, tôi mất rìu của tôi và tái cơ cấu đi bộ cạn của mảng $query['joins'] trong DboSource::renderStatement() vào một phương pháp mới DboSource::buildJoinStatementArray() dẫn đến hai phương pháp:

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; 
} 

Một khi tôi đã DboSource::buildJoinStatementArray(), đã đến lúc phải thay đổi DboSource::buildJoinStatement() - tất cả tôi đã làm được thêm một tấm séc cho $data['joins'] và một phương pháp khác vẽ cho trường hợp đó:

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); 
    } 
} 

Các renderNestedJoinStatement() phương pháp mới là khá giống với 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})"); 
} 
0

Nếu tôi nhận được quyền này, bạn đã có những mối quan hệ sau (hy vọng trong mô hình của bạn):

TableA hasMany TableB. 
TableA hasMany TableC. 

TableB belongsTo TableA. 

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

TableD hasMany TableC. (might be hasOne) 

Nếu bạn đang sử dụng các hành vi Containable (Tôi rất khuyên bạn nên, và đặt nó ở cấp app_model cho tất cả các mô hình kế thừa), tôi nghĩ bạn có thể làm điều gì đó giống như thứ là ...

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

Nếu bạn cần phải chọn lĩnh vực cụ thể, sau đó bạn sẽ cần phải xác định chúng trong tham số chứa, ví dụ ở đây tôi hạn chế lĩnh vực trở TableB của:

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

Các trở dữ liệu cần được như vậy:

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

Tuy nhiên, tôi chưa bao giờ làm điều này nơi các bảng lồng nhau là phụ huynh của container (tabled và TableC), vì vậy nó có thể không hoạt động, nhưng nó có thể là giá trị một thử .

+0

Đây không phải là thực sự những gì một bên ngoài tham gia không. Tôi muốn có các trường rỗng trong [TableC] nếu tham gia (TableC x TableD) trống. Nhưng việc thêm các kết nối lồng nhau vào CakePHP thực sự dễ dàng, hãy xem câu trả lời của riêng tôi. –

+0

Điểm tốt, vui vì bạn đã tìm được một công việc xung quanh. Nó sẽ là tuyệt vời nếu bạn có thể kiểm tra xem giải pháp của bạn có thể áp dụng cho chi nhánh 1.3.x và gửi nó như là một sửa chữa cho trường hợp sử dụng của bạn, tôi chắc chắn có rất nhiều người sẽ được hưởng lợi. – ianmjones

Các vấn đề liên quan