2010-08-21 36 views
9

Bất cứ ai cũng biết cách tốt để thực hiện truy vấn UNION trong CakePHP? Tôi muốn tránh sử dụng $this->query();.Cú pháp UNION trong Cakephp

Với hai bảng t1, t2:

SELECT * FROM t1 
LEFT JOIN t2 ON t1.id = t2.id 
UNION 
SELECT * FROM t1 
RIGHT JOIN t2 ON t1.id = t2.id 

Với ba bảng t1, t2, t3:

SELECT * FROM t1 
LEFT JOIN t2 ON t1.id = t2.id 
LEFT JOIN t3 ON t2.id = t3.id 
UNION 
SELECT * FROM t1 
RIGHT JOIN t2 ON t1.id = t2.id 
LEFT JOIN t3 ON t2.id = t3.id 
UNION 
SELECT * FROM t1 
RIGHT JOIN t2 ON t1.id = t2.id 
RIGHT JOIN t3 ON t2.id = t3.id 
+0

Có bất kỳ lý do tại sao bạn muốn tránh $ this-> truy vấn? – Stoosh

+3

Vâng, đối với một, nó đánh bại mục đích của việc sử dụng một khuôn khổ nếu bạn không cố gắng viết một ứng dụng theo kiểu gốc của khung công tác. Và hầu hết các truy vấn 'SELECT' có thể được thực hiện bằng cách sử dụng' find() 'trong 1.3.Ngoài ra, 'find()' lợi ích từ sự bảo vệ được tích hợp sẵn của trình điều khiển nguồn dữ liệu chống lại việc tiêm SQL, trong khi 'truy vấn()' là một truy vấn trực tiếp mà bạn phải thoát khỏi chính mình. Điều đó nói rằng, 'UNION' có lẽ là một trong số ít các lớp truy vấn mà bạn không thể thực hiện bằng cách sử dụng' find() '. –

+0

Bạn có một ví dụ hợp lệ về MySQL mà bạn đang chạy không? Danh sách được liệt kê không hoạt động. –

Trả lời

12

Quá nhiều lập trình viên cố gắng giới hạn bản thân với chức năng của một khuôn khổ. KHÔNG. Sử dụng những gì khuôn khổ cung cấp. Nếu nó không có các chức năng mà bạn tìm kiếm, sau đó một trong hai:

  • Mã chức năng bạn cần vào một phần mở rộng lớp

hoặc

  • Tuỳ chỉnh quay mã trong khuôn khổ cho phù hợp bạn cần.

Thông thường, các nhà phát triển cố gắng búa một hình vuông vào lỗ tròn và gió lên làm quá nhiều công việc thêm mà thực sự chỉ làm cho mã phức tạp. Hãy lùi lại một bước và hỏi tại sao bạn đang sử dụng khuôn khổ để bắt đầu. Nó mang cấu trúc đến một ngôn ngữ phi cấu trúc. Nó cung cấp nền tảng tái sử dụng vững chắc để xây dựng ứng dụng của bạn trên đó. Nó không phải là một hộp để đặt mình vào và bị hạn chế.

UPDATE: Tôi mất một phút để đọc Complex Find Conditions và tìm thấy câu trả lời của bạn:

$joins = array(
    array(
     'table' => 'test_twos', 
     'alias' => 'TestTwo', 
     'type' => 'LEFT', 
     'conditions' => array(
      'TestTwo.id = TestOne.id', 
     ) 
    ), 
    array(
     'table' => 'test_threes', 
     'alias' => 'TestThree', 
     'type' => 'LEFT', 
     'conditions' => array(
     'TestThree.id = TestOne.id', 
    ) 
    ) 
); 

$dbo = $this->getDataSource(); 
$subQuery = $dbo->buildStatement(
    array(
     'fields' => array('*'), 
     'table' => $dbo->fullTableName($this), 
     'alias' => 'TestOne', 
     'limit' => null, 
     'offset' => null, 
     'joins' => $joins, 
     'conditions' => null, 
     'order' => null, 
     'group' => null 
    ), 
    $this->TestOne 
); 
$query = $subQuery; 

$query .= ' UNION '; 
$joins = array(
    array(
     'table' => 'test_twos', 
     'alias' => 'TestTwo', 
     'type' => 'LEFT', 
     'conditions' => array(
      'TestTwo.id = TestOne.id', 
     ) 
    ), 
    array(
     'table' => 'test_threes', 
     'alias' => 'TestThree', 
     'type' => 'RIGHT', 
     'conditions' => array(
     'TestThree.id = TestOne.id', 
     ) 
    ) 
); 

$dbo = $this->getDataSource(); 
$subQuery = $dbo->buildStatement(
    array(
    'fields' => array('*'), 
    'table' => $dbo->fullTableName($this), 
    'alias' => 'TestOne', 
    'limit' => null, 
    'offset' => null, 
    'joins' => $joins, 
    'conditions' => null, 
    'order' => null, 
    'group' => null 
    ), 
    $this->TestOne 
); 

$query .= $subQuery; 

pr($query); 
+3

Việc đầu tiên cần làm là kiểm tra xem có thể giải quyết vấn đề với chức năng của khung công tác hay không. Tất nhiên, khung công tác không được giới hạn một lập trình viên. – bancer

+0

+1 để cập nhật. Đó là đúng hướng. Tôi sẽ xem xét kỹ hơn mã sau. – bancer

+1

Nhận xét để giới thiệu rằng các nhà phát triển không giới hạn bản thân với chức năng do khung công tác cung cấp. Tôi thấy cách quá nhiều lần mà mọi người gạt bỏ một giải pháp hoàn toàn hợp lệ bởi vì "Nó không phải là Bánh Way." – UncaAlby

3

Sử dụng một cái nhìn, sau đó chọn từ đó:

create view my_union as 
SELECT * FROM t1 
LEFT JOIN t2 ON t1.id = t2.id 
LEFT JOIN t3 ON t2.id = t3.id 
UNION 
SELECT * FROM t1 
RIGHT JOIN t2 ON t1.id = t2.id 
LEFT JOIN t3 ON t2.id = t3.id 
UNION 
SELECT * FROM t1 
RIGHT JOIN t2 ON t1.id = t2.id 
RIGHT JOIN t3 ON t2.id = t3.id 

Trong mã của bạn:

select * from my_union 
+1

Đây là một giải pháp tốt có thể, nhưng hãy xem tuyên bố từ chối trách nhiệm này khi sử dụng chế độ xem trong Bánh: http://www.mail-archive.com/[email protected]/msg43116.html –

+0

Tôi sợ rằng sẽ không hoạt động các cơ sở dữ liệu khác như SQLlite. – bancer

+0

@bancer: bạn có chắc chắn về điều đó không? Tài liệu của SQLite chỉ ra rằng nó hỗ trợ các khung nhìn và sử dụng cú pháp 'create view' giống nhau. –

3

Một cách đơn giản để làm điều này, mà chúng tôi hiện đang sử dụng, là để tạo ra một cái nhìn trong MySQL hoặc bất cứ cơ sở dữ liệu bạn sử dụng. Sau đó, thay vì sử dụng bảng trong mô hình của bạn, bạn sử dụng chế độ xem của mình. Bạn có thể đọc về cú pháp tạo chế độ xem tại đây: http://dev.mysql.com/doc/refman/5.6/en/create-view.html. Bạn cũng có thể sử dụng phần mềm như HeidiSQL để giúp bạn tạo ra chế độ xem.

Sau đó bạn sẽ có một cái gì đó như thế này trong mô hình của bạn:

class Contenu extends AppModel { 
    public $useTable = 'v_contenu'; 

này cho phép bạn vẫn sử dụng phương pháp find() trong CakePHP, đó là thật sự tốt đẹp để có.

Để có hiệu suất tốt nhất với chế độ xem, bạn nên cập nhật MySQL lên ít nhất là phiên bản 5.6.

+0

Đây là một giải pháp thực sự tốt đẹp – Will

0

sử dụng một mã như thế này:

$friendsPosts= $this->Posts->find('all') 
       ->contain(['Users', 'Languages', 'PostStates']) 
       ->innerJoinWith('Users.Dusers', function ($q) { 
        return $q->where(['Dusers.id' => $this->Auth->user('id')]); 
       }); 

     $posts= $this->Posts->find('all') 
       ->where(['Posts.post_state_id' => 3]) 
       ->contain(['Users', 'Languages', 'PostStates']); 

    $posts->union($friendsPosts);