Các truy vấn cơ sở dữ liệu nên được trừu tượng hóa ra khỏi trang?


10

Khi viết thế hệ trang bằng PHP, tôi thường thấy mình viết một tập các tệp chứa đầy các truy vấn cơ sở dữ liệu. Ví dụ: tôi có thể có một truy vấn để tìm nạp một số dữ liệu về một bài đăng trực tiếp từ cơ sở dữ liệu để hiển thị trên một trang, như thế này:

$statement = $db->prepare('SELECT * FROM posts WHERE id=:id');
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$post = $statement->fetch(PDO::FETCH_ASSOC);
$content = $post['content']
// do something with the content

Các truy vấn nhanh, một lần này thường nhỏ, nhưng đôi khi tôi kết thúc với một phần lớn mã tương tác cơ sở dữ liệu bắt đầu trông khá lộn xộn.

Trong một số trường hợp, tôi đã giải quyết vấn đề này bằng cách tạo một thư viện hàm đơn giản để xử lý các truy vấn db liên quan đến bài đăng của mình, rút ​​ngắn khối mã đó thành đơn giản:

$content = post_get_content($id);

Và điều đó thật tuyệt. Hoặc ít nhất là cho đến khi tôi cần phải làm một cái gì đó khác. Có lẽ tôi cần phải có năm bài viết gần đây nhất để hiển thị trong một danh sách. Vâng, tôi luôn có thể thêm một chức năng khác:

$recent_posts = post_get_recent(5);
foreach ($recent_posts as $post) { ... }

Nhưng điều đó kết thúc bằng một SELECT *truy vấn, điều mà tôi thường thực sự không cần dù sao, nhưng thường quá phức tạp để trừu tượng hợp lý. Cuối cùng tôi kết thúc với một thư viện lớn các hàm tương tác cơ sở dữ liệu cho mỗi trường hợp sử dụng hoặc một loạt các truy vấn lộn xộn bên trong mã của mỗi trang. Và ngay cả khi tôi đã xây dựng các thư viện này, tôi sẽ thấy mình cần phải thực hiện một tham gia nhỏ mà tôi chưa từng sử dụng trước đây và đột nhiên tôi cần phải viết một chức năng chuyên môn cao khác để thực hiện công việc.

Chắc chắn, tôi có thể sử dụng các hàm cho các trường hợp sử dụng chung và truy vấn cho các tương tác cụ thể, nhưng ngay khi tôi bắt đầu viết các truy vấn thô, tôi bắt đầu quay lại truy cập trực tiếp vào mọi thứ. Dù vậy, hoặc tôi sẽ trở nên lười biếng và sẽ bắt đầu thực hiện mọi thứ trong các vòng lặp PHP thực sự nên được thực hiện trực tiếp trong các truy vấn MySQL.

Tôi muốn hỏi những người có kinh nghiệm hơn với việc viết các ứng dụng internet: việc tăng khả năng duy trì có xứng đáng với các dòng mã bổ sung và sự thiếu hiệu quả có thể có mà các tóm tắt có thể đưa ra không? Hoặc chỉ đơn giản là sử dụng chuỗi truy vấn trực tiếp là một phương pháp có thể chấp nhận để xử lý các tương tác cơ sở dữ liệu?


Có lẽ bạn có thể sử dụng các quy trình được lưu trữ để "bọc" mớ hỗn độn select- bạn sẽ chỉ phải gọi các quy trình đó với một số tham số bạn cần
k102

Câu trả lời:


7

Khi bạn có quá nhiều hàm truy vấn chuyên biệt, bạn có thể thử chia chúng thành các bit có thể ghép lại được. Ví dụ

$posts = posts()->joinWithComments()->orderBy("post.post_date")->first(5);

Ngoài ra còn có một hệ thống phân cấp các mức độ trừu tượng mà bạn có thể thấy hữu ích để ghi nhớ. Bạn có

  1. API mysql
  2. các chức năng mysql của bạn , chẳng hạn như select ("select * từ các bài đăng trong đó foo = bar"); hoặc có thể nhiều hơn nhưselect("posts")->where("foo = bar")->first(5)
  3. chẳng hạn như các chức năng dành riêng cho miền ứng dụng của bạn posts()->joinWithComments()
  4. các chức năng dành riêng cho một trang cụ thể, chẳng hạn như commentsToBeReviewed($currentUser)

Nó trả rất nhiều về mặt dễ bảo trì để tôn trọng thứ tự trừu tượng này. Các tập lệnh trang chỉ nên sử dụng các chức năng cấp 4, các chức năng cấp 4 nên được viết theo các chức năng cấp 3, v.v. Đúng là điều này cần thêm một chút thời gian trả trước nhưng nó sẽ giúp giữ cho chi phí bảo trì của bạn không đổi theo thời gian (trái ngược với "trời ơi, họ muốn có một sự thay đổi khác !!!")


2
+1 Cú pháp này về cơ bản tạo ORM của riêng bạn. Nếu bạn thực sự lo lắng về việc truy cập cơ sở dữ liệu trở nên phức tạp và không muốn mất nhiều thời gian để tìm hiểu chi tiết, tôi khuyên bạn nên sử dụng khung web trưởng thành (ví dụ CodeIgniter ) đã tìm ra công cụ này. Hoặc ít nhất hãy thử sử dụng nó để xem loại đường cú pháp nào mang lại cho bạn, tương tự như những gì xpmatteo đã thể hiện.
Hartley Brody

5

Tách các mối quan tâm là một nguyên tắc đáng đọc, xem bài viết Wikipedia về nó.

http://en.wikipedia.org/wiki/Separation_of_concerns

Một nguyên tắc khác đáng đọc là Khớp nối:

http://en.wikipedia.org/wiki/Coupling_(computer_science )

Bạn có hai mối quan tâm riêng biệt, một là việc sắp xếp dữ liệu từ cơ sở dữ liệu và thứ hai là kết xuất dữ liệu đó. Trong một ứng dụng thực sự đơn giản, có lẽ không có gì phải lo lắng, bạn đã kết hợp chặt chẽ lớp truy cập và quản lý cơ sở dữ liệu của mình với lớp kết xuất nhưng đối với các ứng dụng nhỏ thì đây không phải là vấn đề lớn. Vấn đề là các ứng dụng web có xu hướng phát triển và nếu bạn muốn mở rộng một ứng dụng web, bằng bất kỳ cách nào, tức là hiệu suất hoặc chức năng, thì bạn gặp phải một số vấn đề.

Hãy nói rằng bạn đang tạo một trang web các bình luận do người dùng tạo. Cùng với ông chủ tóc nhọn và yêu cầu bạn bắt đầu hỗ trợ Ứng dụng gốc, ví dụ như iPhone / Android, v.v. Chúng tôi cần một số đầu ra JSON, bây giờ bạn cần trích xuất mã kết xuất đang tạo HTML. Khi bạn đã hoàn thành việc này, giờ bạn đã có một thư viện truy cập dữ liệu với hai công cụ kết xuất và mọi thứ đều ổn, bạn đã thu nhỏ theo chức năng. Bạn thậm chí có thể quản lý để giữ mọi thứ riêng biệt, ví dụ logic kinh doanh khỏi kết xuất.

Sếp đến và nói với bạn rằng anh ta có một khách hàng muốn hiển thị các bài đăng trên trang web của họ, họ cần XML và họ cần khoảng 5000 yêu cầu mỗi giây hiệu suất cao nhất. Bây giờ bạn cần tạo XML / JSON / HTML. Bạn có thể tách ra kết xuất của bạn một lần nữa, giống như trước đây. Tuy nhiên, bây giờ bạn cần thêm 100 máy chủ để thoải mái có được hiệu suất bạn cần. Bây giờ cơ sở dữ liệu của bạn đang bị tấn công từ 100 máy chủ với hàng chục kết nối trên mỗi máy chủ, mỗi máy chủ được tiếp xúc trực tiếp với ba ứng dụng khác nhau với các yêu cầu khác nhau và các truy vấn khác nhau, v.v. Có quyền truy cập cơ sở dữ liệu trên mỗi máy frontend là một rủi ro bảo mật và ngày càng tăng một nhưng tôi sẽ không đến đó. Bây giờ bạn cần mở rộng quy mô cho hiệu suất, mỗi ứng dụng có các yêu cầu bộ nhớ đệm khác nhau, tức là những mối quan tâm khác nhau. Bạn có thể thử và quản lý điều này trong một lớp kết hợp chặt chẽ, tức là lớp truy cập / logic nghiệp vụ / kết xuất cơ sở dữ liệu của bạn. Mối quan tâm của mỗi lớp hiện đang bắt đầu cản trở nhau, tức là các yêu cầu lưu trữ dữ liệu từ cơ sở dữ liệu có thể rất khác so với lớp kết xuất, logic mà bạn có trong lớp nghiệp vụ có thể bị chảy vào SQL tức là di chuyển ngược hoặc nó có thể chảy vào lớp kết xuất, đây là một trong những vấn đề lớn nhất tôi gặp phải khi có mọi thứ trong một lớp, nó giống như đổ bê tông cốt thép vào ứng dụng của bạn chứ không phải theo cách tốt.

Có nhiều cách tiêu chuẩn để tiếp cận các loại vấn đề này, ví dụ như bộ nhớ đệm HTTP của các dịch vụ web (mực / yts, v.v.). Bộ nhớ đệm cấp ứng dụng trong chính các dịch vụ web với một cái gì đó như memcached / redis. Bạn cũng sẽ gặp phải các vấn đề khi bạn bắt đầu mở rộng quy mô cơ sở dữ liệu của mình, tức là nhiều máy chủ đọc và một chủ hoặc dữ liệu được phân chia trên các máy chủ. Bạn không muốn 100 máy chủ quản lý các kết nối khác nhau đến cơ sở dữ liệu của mình khác nhau dựa trên các yêu cầu ghi hoặc đọc hoặc trong cơ sở dữ liệu bị phân tách nếu người dùng, người dùng, người dùng băm vào trong [bảng / cơ sở dữ liệu] cho tất cả các yêu cầu ghi.

Tách biệt các mối quan tâm là bạn của bạn, lựa chọn thời điểm và nơi để thực hiện nó là một quyết định kiến ​​trúc và một chút nghệ thuật. Tránh khớp nối chặt chẽ bất cứ thứ gì sẽ phát triển để có những yêu cầu rất khác nhau. Có một loạt các lý do khác để giữ mọi thứ riêng biệt, nghĩa là nó đơn giản hóa việc kiểm tra, triển khai các thay đổi, bảo mật, tái sử dụng, tính linh hoạt, v.v.


Tôi hiểu bạn đến từ đâu và tôi không đồng ý với bất cứ điều gì bạn đã nói, nhưng đây là một mối quan tâm nhỏ ngay bây giờ. Dự án được đề cập là một dự án cá nhân và hầu hết vấn đề của tôi với mô hình hiện tại của tôi xuất phát từ bản năng lập trình viên của tôi để tránh sự liên kết chặt chẽ, nhưng tôi thực sự là một người mới phát triển phía máy chủ phức tạp, vì vậy kết thúc này đã đi đôi chút trên đầu của tôi. Tuy nhiên, +1 cho những gì dường như là lời khuyên tốt cho tôi, ngay cả khi tôi có thể không hoàn toàn làm theo nó cho dự án này.
Vua Alexis

Nếu những gì bạn đang làm sẽ còn nhỏ thì tôi sẽ giữ nó đơn giản nhất có thể. Một nguyên tắc tốt để làm theo ở đây là YAGNI .
Harry

1

Tôi giả sử rằng khi bạn nói "chính trang đó", bạn có nghĩa là tệp nguồn PHP tự động tạo HTML.

Không truy vấn cơ sở dữ liệu và tạo HTML trong cùng một tệp nguồn.

Tệp nguồn nơi bạn truy vấn cơ sở dữ liệu không phải là "trang", mặc dù đó là tệp nguồn PHP.

Trong tệp nguồn PHP nơi bạn tự động tạo HTML, bạn chỉ cần thực hiện các cuộc gọi đến các hàm được xác định trong tệp nguồn PHP nơi cơ sở dữ liệu được truy cập.


0

Mẫu tôi sử dụng cho hầu hết các dự án quy mô trung bình là mẫu sau:

  • Tất cả các truy vấn SQL được đặt ngoài mã phía máy chủ, ở một vị trí riêng biệt.

    Làm việc với C #, có nghĩa là sử dụng các lớp một phần, tức là đặt các truy vấn vào một tệp riêng, với điều kiện là các truy vấn đó sẽ có thể truy cập được từ một lớp duy nhất (xem mã bên dưới).

  • Các truy vấn SQL là hằng số . Điều này rất quan trọng, vì nó ngăn chặn sự cám dỗ của việc xây dựng các truy vấn SQL một cách nhanh chóng (do đó làm tăng nguy cơ tiêm SQL và đồng thời khiến việc xem xét các truy vấn sau này trở nên khó khăn hơn).

Cách tiếp cận hiện tại trong C #

Thí dụ:

// Demo.cs
public partial class Demo : DataRepository
{
    public IEnumerable<Stuff> LoadStuff(int categoryId)
    {
        return this
            .Query(Queries.LoadStuff)
            .With(new { CategoryId = categoryId })
            .ReadRows<Stuff>();
    }

    // Other methods go here.
}

public partial class Demo
{
    private static class Queries
    {
        public const string LoadStuff = @"
select top 100 [StuffId], [SomeText]
    from [Schema].[Table]
    where [CategoryId] = @CategoryId
    order by [CreationUtcTime]";

        // Other queries go here.
    }
}

Cách tiếp cận này có lợi ích là có các truy vấn trong một tệp riêng biệt. Điều này cho phép DBA xem xét và sửa đổi / tối ưu hóa các truy vấn và cam kết tệp đã sửa đổi thành kiểm soát nguồn mà không xung đột với các cam kết được thực hiện bởi các nhà phát triển.

Một lợi ích liên quan là kiểm soát nguồn có thể được cấu hình theo cách giới hạn quyền truy cập của DBA chỉ với các tệp có chứa các truy vấn và từ chối quyền truy cập vào phần còn lại của mã.

Có thể làm trong PHP?

PHP thiếu cả các lớp một phần và các lớp bên trong, do đó, nó không thể được thực hiện trong PHP.

Bạn có thể tạo một tệp riêng với một lớp tĩnh ( DemoQueries) chứa các hằng số, với điều kiện là lớp đó sẽ có thể truy cập được từ mọi nơi. Hơn nữa, để tránh gây ô nhiễm phạm vi toàn cầu, bạn có thể đặt tất cả các lớp truy vấn trong một không gian tên dành riêng. Điều này sẽ tạo ra một cú pháp khá dài dòng, nhưng tôi nghi ngờ bạn có thể tránh được tính dài dòng:

namespace Data {
    public class Demo inherit DataRepository {
        public function LoadStuff($categoryId) {
            $query = \Queries\Demo::$LoadStuff;
            // Do the stuff with the query.
        }

        // Other methods go here.
    }
}

namespace Queries {
    public static class Demo {
        public const $LoadStuff = '...';

        // Other queries go here.
    }
}
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.