Phương thức tạo truy vấn con bằng cách sử dụng JDatabase


31

Tại http: //docs.j Joomla.org/Selecting_data_USE_JDatabase, không có một phương pháp tài liệu nào để viết một truy vấn con bằng cách sử dụng JDatabase.

https://gist.github.com/gunjanpatel/8663333 minh họa một cách để thực hiện điều này với (một vài bit bị bỏ qua):

$subQuery = $db->getQuery(true);
$query    = $db->getQuery(true);

// Create the base subQuery select statement.
$subQuery->select('*')
    ->from($db->quoteName('#__sub_table'))
    ->where($db->quoteName('subTest') . ' = ' . $db->quote('1'));

// Create the base select statement.
$query->select('*')
    ->from($db->quoteName('#__table'))
    ->where($db->quoteName('state') . ' = ' . $db->quote('1'))
    ->where($db->quoteName('subCheckIn') . ' IN (' . $subQuery->__toString() . ')')
    ->order($db->quoteName('ordering') . ' ASC');

// Set the query and load the result.
$db->setQuery($query);

Đây có vẻ là một cách tiếp cận tốt, hợp lý, nhưng có cách nào tốt hơn không?


4
Bạn có thể bỏ qua việc gọi toString () trên $ subQuery. Joomla! sẽ tự động xử lý nó cho bạn. Bên cạnh đó, tôi sử dụng phương pháp tương tự và nó hoạt động tốt với tôi.
Zachary Draper

Đây cũng là phương pháp tương tự mà chúng tôi đang sử dụng trong com_content trong lõi github.com/j Joomla / j Joomla
Wilson Wilson

@ZacharyDraper thú vị. Bạn có thể hiển thị mã chịu trách nhiệm cho điều đó?
Dmitry Rekun

3
@ZacharyDraper: PHP (chứ không phải Joomla! Per se) xử lý nó cho bạn ( __toString()) là một phương thức "ma thuật".
MrWhite

Vâng, cảm ơn bạn w3d.
Zachary Draper

Câu trả lời:


16

Vâng, theo như tôi nghĩ, cách bạn xây dựng truy vấn con là cách mà phần lớn các nhà phát triển tiện ích mở rộng của j Joomla chấp nhận.

Tôi sử dụng phương pháp tương tự trên một số tiện ích mở rộng và tiện ích mở rộng tùy chỉnh được tạo cho khách hàng.

Không có cách "chính thức" để làm việc này, nhưng thực hiện như bạn đã trình bày cho phép bạn sử dụng trình tạo truy vấn và vẫn giữ được mức độ dễ đọc


10

AFAIK không có cách xây dựng để thực hiện các truy vấn con dễ dàng, có lẽ là một thiếu sót trong hệ thống và cần được sửa chữa thông qua PR.

Tuy nhiên, tôi thấy không có vấn đề gì với ví dụ của bạn - có vẻ đủ hợp lý.

~~~

Dưới đây là một ví dụ để phản hồi bình luận của @ DavidFritsch bên dưới. Càng nghĩ nhiều về nó, tôi càng thích cách tiếp cận đơn giản hơn được hiển thị trong OP. Rõ ràng hơn những gì đang xảy ra.

$query = $this->db->getQuery(true)
  ->select('a.*')
  ->subQuery()
    ->select('b.*')
    ->from('#__table_b AS b')
    ->as('subQueryResult')
  ->endSubQuery()
  ->from('#__table_a AS a');

1
Bạn có biết làm thế nào điều này có thể được thực hiện để làm việc? Tôi đang cố gắng tưởng tượng định dạng mà bạn sẽ sử dụng để làm cho công việc này trên một đối tượng truy vấn và không có gì thực sự cảm thấy dễ dàng hơn phương pháp này.
David Fritsch

1
Có thể đáng để tạo ra một subQuerySelectphương thức trong đó nó cho phép bạn thực hiện nó một cách "sạch sẽ" hơn một chút. Tôi sẽ chỉnh sửa câu trả lời của tôi để cung cấp và ví dụ.
Don Gilbert

Tôi rất thích thấy điều đó trong Joomla
fruppel

3

Ngoài ra còn có một cách để thực hiện các truy vấn có chứa các truy vấn con bằng API nền tảng Joomla. Ý tưởng cơ bản về cách sử dụng các truy vấn con dựa trên gunjanpatel .

Dưới đây là một ví dụ để thực hiện các truy vấn trên Mô hình tập hợp lồng nhau :

Truy vấn SQL:

-- Find the Immediate Subordinates of a Node
SELECT node.title, (COUNT(parent.id) - (sub_tree.depth + 1)) AS depth
FROM lubd3_usergroups AS node,
        lubd3_usergroups AS parent,
        lubd3_usergroups AS sub_parent,
        (
                SELECT node.id, (COUNT(parent.id) - 1) AS depth
                FROM lubd3_usergroups AS node,
                        lubd3_usergroups AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.id = 1
                GROUP BY node.id
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.id = sub_tree.id
GROUP BY node.id
-- not showing the parent node
HAVING depth = 1
-- showing the parent node
-- HAVING depth <= 1
ORDER BY node.lft;

và truy vấn được chuyển đổi sẽ được Joomla thực hiện:

// Create the subQuery select statement.
// Nested Set Queries: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
// CROSS JOIN: http://www.informit.com/articles/article.aspx?p=30875&seqNum=5
$subQuery->select(array('node.id', '(COUNT(parent.id) - 1) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt') . ' AND ' . $db->quoteName('node.id') . ' = ' . $db->quote('1'))
    ->group($db->quoteName('node.id'))
    ->order($db->quoteName('node.lft'));

// Create the base select statement.
$query->select(array('node.title', '(COUNT(parent.id) - (sub_tree.depth + 1)) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->join('CROSS', $db->quoteName('#__usergroups', 'sub_parent'))
    ->join('CROSS', '(' . $subQuery .') AS sub_tree')
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt')
    . ' AND ' . $db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('sub_parent.lft') . ' AND ' . $db->quoteName('sub_parent.rgt')
    . ' AND ' . $db->quoteName('sub_parent.id') . ' = ' . $db->quoteName('sub_tree.id'))
    ->group($db->quoteName('node.id'))
    ->having($db->quoteName('depth') . ' = ' . $db->quote('1'))
    ->order($db->quoteName('node.lft'));

// Set the query and load the result.
$db->setQuery($query);
$rowList = $db->loadAssocList();

echo "<pre>";
print_r($rowList);
echo "</pre>";

1
Có vẻ tốt nhưng hoàn toàn giống như trong ví dụ của OP: Trước tiên hãy thực hiện truy vấn con và sử dụng nó trong truy vấn chính. Câu hỏi là nếu có một cách tốt hơn.
fruppel

1

Tôi sẽ cung cấp phiên bản đoạn mã của mình sau đó giải thích lời biện minh của mình và bao gồm các trích dẫn từ hướng dẫn Tiêu chuẩn Mã hóa Joomla (sẽ được định dạng theo định dạng).

$subquery = $db->getQuery(true)
    ->select("checkin")
    ->from("#__sub_table")
    ->where("subTest = 1");

$query = $db->getQuery(true)
    ->select("*")
    ->from("#__table")
    ->where([
        "state = 1",
        "subCheckIn IN ({$subQuery})"
    ])
    ->order("ordering");

$db->setQuery($query);

Sử dụng chuỗi truy vấn để kết nối một số phương thức truy vấn, lần lượt từng phương thức, với mỗi phương thức trả về một đối tượng có thể hỗ trợ phương thức tiếp theo, Điều này giúp cải thiện khả năng đọc và đơn giản hóa mã kết quả.

  • Tôi viết các truy vấn trong cùng trước và tiến tới truy vấn ngoài cùng. Điều này cho phép tôi chuỗi tất cả các phương thức xây dựng truy vấn trực tiếp đến getQuery()phương thức. Thực tế, tên biến chỉ được viết một lần trong khi xây dựng truy vấn riêng.
    Đây là một ví dụ tuyệt vời về một số truy vấn nặng nề lồng nhau (khi tôi nghĩ thật dễ thương khi xếp các mũi tên xích).

  • Tôi cố gắng tránh thực hiện nhiều cuộc gọi select()và / hoặc where()trong cùng một truy vấn vì tôi thấy điều đó dẫn đến sự nhầm lẫn của các nhà phát triển ít kinh nghiệm hơn . Bởi vì các phương thức này chấp nhận mảng, tôi thấy nó dễ đọc hơn và thực hành mã hóa tốt hơn để sử dụng chúng.

  • và cuối cùng là chủ đề gây tranh cãi nhất ...

    Tên bảng và tên cột bảng phải luôn được đặt trong phương thức quoteName () để thoát tên bảng và cột bảng. Các giá trị trường được kiểm tra trong một truy vấn phải luôn được đặt trong phương thức quote () để thoát giá trị trước khi chuyển nó vào cơ sở dữ liệu. Các giá trị trường số nguyên được kiểm tra trong một truy vấn cũng phải là kiểu truyền tới (int).

    Tôi rất mâu thuẫn về lập trường này. Khi tôi lần đầu tiên đến Joomla năm ngoái, tôi đã nghĩ, tôi sẽ không thực hiện các cuộc gọi vô ích (không có lợi cho sự ổn định, bảo mật, khả năng đọc của truy vấn) trên các giá trị tĩnh! Tuy nhiên, chủ nhân của tôi thích ý tưởng của toeing dòng Joomla, và tôi phải thừa nhận rằng tôi thường có một sự đánh giá cao đối với các quy tắc, vì vậy tôi đã được hosing xuống truy vấn của tôi với quote(), (int)quoteName()đó cũng có nghĩa là đống nối chuỗi (tất cả cách đều nhau). Kết quả cuối cùng của công việc của tôi là các khối truy vấn cồng kềnh khủng khiếp mà ngay cả tôi cũng có một thời gian khó khăn. Các dòng tệ nhất / dài nhất không cho phép xếp theo chiều dọc là các join()cuộc gọi vì tablename, bí danh ON, sau đó một hoặc nhiều điều kiện có thể hoặc không cần trích dẫn.Tôi có thể đánh giá cao rằng chính sách này được triển khai với mục đích bảo mật cho các nhà phát triển mới làm quen, nhưng tôi chắc chắn sẽ thích nó nếu chính sách này bằng cách nào đó được tiết chế với sự nhạy cảm rằng không phải tất cả các lập trình viên Joomla đều là những người sao chép không biết gì. Ý tôi là, hãy xem cách mã sạch và ngắn gọn mà không cần các cuộc gọi không cần thiết.

  • Đối với việc lau dọn:

    • Tôi gần như không bao giờ sử dụng *trong các mệnh đề CHỌN của tôi
    • Tôi không bao giờ gọi __toString()
    • Tôi không trích dẫn số nguyên, tôi chọn chúng là số nguyên
    • Tôi không viết ASCvì đó là hướng sắp xếp mặc định
    • Tôi cố gắng hết sức để không sử dụng từ khóa mysql khi tạo tên bảng và tên cột mới
    • Vì vấn đề sở thích cá nhân, tôi có xu hướng sử dụng trích dẫn kép trên các đối số chuỗi của phương thức của mình để duy trì tính đồng nhất, phân biệt với trích dẫn đơn của mysql và để tôi có thể thưởng thức phép nội suy biến mà tôi thường viết bằng " cú pháp phức tạp ".
    • Tôi sử dụng tên biến thông tin và nhận xét để hỗ trợ khả năng đọc các truy vấn lồng nhau và mã của tôi nói chung
    • Tôi kiểm tra mã của mình trước khi nó rời khỏi quyền giám hộ của tôi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.