Làm thế nào một mô hình nên được cấu trúc trong MVC? [đóng cửa]


551

Tôi mới chỉ nắm bắt được khung công tác MVC và tôi thường tự hỏi nên có bao nhiêu mã trong mô hình. Tôi có xu hướng có một lớp truy cập dữ liệu có các phương thức như thế này:

public function CheckUsername($connection, $username)
{
    try
    {
        $data = array();
        $data['Username'] = $username;

        //// SQL
        $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE Username = :Username";

        //// Execute statement
        return $this->ExecuteObject($connection, $sql, $data);
    }
    catch(Exception $e)
    {
        throw $e;
    }
}

Các mô hình của tôi có xu hướng là một lớp thực thể được ánh xạ tới bảng cơ sở dữ liệu.

Đối tượng mô hình có nên có tất cả các thuộc tính ánh xạ cơ sở dữ liệu cũng như mã ở trên không hoặc có thể tách mã đó ra mà cơ sở dữ liệu thực sự hoạt động không?

Tôi sẽ kết thúc có bốn lớp?


133
Tại sao bạn bắt ngoại lệ chỉ để ném chúng một lần nữa?
Bailey Parker

9
@Elias Van Ootegem: bạn đã bỏ lỡ điểm. Thật vô nghĩa khi bắt chúng trong trường hợp này.
Karoly Horvath

4
@Elias Van Ootegem: hả? nếu nó hoạt động với suy nghĩ lại, điều đó có nghĩa là một lớp trên bắt được ngoại lệ. Nhưng nếu có một cái, thì nó sẽ bắt được nó mà không cần phải suy nghĩ lại vô nghĩa ... (nếu bạn vẫn không nhận được, vui lòng giả lập một mã kiểm tra nhỏ)
Karoly Horvath

3
@Elias Van Ootegem: Tôi không biết bạn đang nói về cái gì, không xử lý một ngoại lệ trên một lớp cụ thể không có nghĩa là nó sẽ dừng ứng dụng. vui lòng xây dựng (hoặc chính xác hơn: không xây dựng) một ví dụ mã trong đó việc thiết lập lại là cần thiết. làm ơn dừng cuộc trò chuyện ngoài lề này đi, làm ơn
Karoly Horvath

6
@drrcknlsn: đó là một đối số hợp lệ, nhưng trong trường hợp đó ít nhất cũng bắt được ngoại lệ mà bạn dự kiến ​​sẽ bị ném, cái chung Exceptionkhông có nhiều giá trị tài liệu. Cá nhân nếu tôi đi trên con đường đó, tôi sẽ chọn PHPDoc @exception, hoặc một số cơ chế tương tự, để nó xuất hiện trong tài liệu được tạo.
Karoly Horvath

Câu trả lời:


903

Tuyên bố miễn trừ trách nhiệm: sau đây là mô tả về cách tôi hiểu các mẫu giống như MVC trong ngữ cảnh của các ứng dụng web dựa trên PHP. Tất cả các liên kết bên ngoài được sử dụng trong nội dung đều có để giải thích các thuật ngữ và khái niệm và không ngụ ý uy tín của riêng tôi về chủ đề này.

Điều đầu tiên mà tôi phải làm rõ là: mô hình là một lớp .

Thứ hai: có một sự khác biệt giữa MVC cổ điển và những gì chúng ta sử dụng trong phát triển web. Đây là một chút câu trả lời cũ hơn mà tôi đã viết, mô tả ngắn gọn về sự khác biệt của chúng.

Mô hình là gì KHÔNG:

Mô hình không phải là một lớp hoặc bất kỳ đối tượng nào. Đó là một lỗi rất phổ biến (tôi cũng vậy, mặc dù câu trả lời ban đầu được viết khi tôi bắt đầu học theo cách khác) , bởi vì hầu hết các khuôn khổ đều duy trì quan niệm sai lầm này.

Đây không phải là một kỹ thuật Ánh xạ quan hệ đối tượng (ORM) cũng không phải là sự trừu tượng hóa của các bảng cơ sở dữ liệu. Bất cứ ai nói với bạn khác rất có thể đang cố gắng 'bán' một ORM hoàn toàn mới hoặc toàn bộ khung.

Mô hình là gì:

Trong sự thích nghi MVC thích hợp, M chứa tất cả các logic kinh doanh tên miền và các mẫu lớpchủ yếu được làm từ ba loại cấu trúc:

  • Đối tượng miền

    Một đối tượng miền là một thùng chứa logic của thông tin miền thuần túy; nó thường đại diện cho một thực thể logic trong không gian miền vấn đề. Thường được gọi là logic kinh doanh .

    Đây sẽ là nơi bạn xác định cách xác thực dữ liệu trước khi gửi hóa đơn hoặc để tính tổng chi phí của đơn hàng. Đồng thời, các Đối tượng Miền hoàn toàn không biết về lưu trữ - không phải từ đâu (cơ sở dữ liệu SQL, API REST, tệp văn bản, v.v.) cũng như ngay cả khi chúng được lưu hoặc truy xuất.

  • Trình ánh xạ dữ liệu

    Những đối tượng này chỉ chịu trách nhiệm cho việc lưu trữ. Nếu bạn lưu trữ thông tin trong cơ sở dữ liệu, đây sẽ là nơi SQL sống. Hoặc có thể bạn sử dụng tệp XML để lưu trữ dữ liệu và Trình ánh xạ dữ liệu của bạn đang phân tích cú pháp từ và sang tệp XML.

  • Dịch vụ

    Bạn có thể coi chúng là "Đối tượng miền cấp cao hơn", nhưng thay vì logic nghiệp vụ , Dịch vụ chịu trách nhiệm tương tác giữa Đối tượng miền và Trình ánh xạ . Các cấu trúc này cuối cùng tạo ra một giao diện "công khai" để tương tác với logic nghiệp vụ miền. Bạn có thể tránh chúng, nhưng với hình phạt rò rỉ một số logic miền vào Bộ điều khiển .

    Có một câu trả lời liên quan đến chủ đề này trong câu hỏi triển khai ACL - nó có thể hữu ích.

Giao tiếp giữa lớp mô hình và các phần khác của bộ ba MVC chỉ nên diễn ra thông qua Dịch vụ . Sự tách biệt rõ ràng có một vài lợi ích bổ sung:

  • nó giúp thực thi nguyên tắc trách nhiệm duy nhất (SRP)
  • cung cấp thêm 'phòng ngọ nguậy' trong trường hợp logic thay đổi
  • giữ cho bộ điều khiển càng đơn giản càng tốt
  • đưa ra một kế hoạch chi tiết rõ ràng, nếu bạn cần một API bên ngoài

 

Làm thế nào để tương tác với một mô hình?

Điều kiện tiên quyết: xem các bài giảng "Nhà nước và người độc thân toàn cầu""Đừng tìm kiếm những điều!" từ các cuộc thảo luận về mã sạch.

Đạt được quyền truy cập vào các trường hợp dịch vụ

Đối với cả phiên bản Chế độ xem và Trình điều khiển (cái mà bạn có thể gọi: "Lớp UI") để truy cập các dịch vụ này, có hai cách tiếp cận chung:

  1. Bạn có thể tiêm trực tiếp các dịch vụ cần thiết vào các hàm tạo của các khung nhìn và bộ điều khiển của bạn, tốt nhất là sử dụng bộ chứa DI.
  2. Sử dụng một nhà máy cho các dịch vụ như một sự phụ thuộc bắt buộc cho tất cả các quan điểm và bộ điều khiển của bạn.

Như bạn có thể nghi ngờ, container DI là một giải pháp thanh lịch hơn rất nhiều (trong khi không phải là dễ nhất cho người mới bắt đầu). Hai thư viện mà tôi khuyên bạn nên xem xét cho chức năng này sẽ là thành phần DependencyInjection độc lập của Syfmony hoặc Auryn .

Cả hai giải pháp sử dụng nhà máy và bộ chứa DI sẽ cho phép bạn cũng chia sẻ các phiên bản của các máy chủ khác nhau được chia sẻ giữa bộ điều khiển được chọn và chế độ xem cho một chu kỳ đáp ứng yêu cầu đã cho.

Thay đổi trạng thái của mô hình

Bây giờ bạn có thể truy cập vào lớp mô hình trong bộ điều khiển, bạn cần bắt đầu thực sự sử dụng chúng:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $identity = $this->identification->findIdentityByEmailAddress($email);
    $this->identification->loginWithPassword(
        $identity,
        $request->get('password')
    );
}

Bộ điều khiển của bạn có một nhiệm vụ rất rõ ràng: lấy đầu vào của người dùng và, dựa trên đầu vào này, thay đổi trạng thái logic kinh doanh hiện tại. Trong ví dụ này, các trạng thái được thay đổi giữa là "người dùng ẩn danh" và "người dùng đã đăng nhập".

Trình điều khiển không chịu trách nhiệm xác thực đầu vào của người dùng, vì đó là một phần của quy tắc kinh doanh và trình điều khiển chắc chắn không gọi các truy vấn SQL, giống như những gì bạn sẽ thấy ở đây hoặc ở đây (xin đừng ghét chúng, chúng bị nhầm lẫn, không phải là xấu xa).

Hiển thị cho người dùng thay đổi trạng thái.

Ok, người dùng đã đăng nhập (hoặc thất bại). Giờ thì sao?Người dùng cho biết vẫn không biết về nó. Vì vậy, bạn cần phải thực sự tạo ra một phản hồi và đó là trách nhiệm của một quan điểm.

public function postLogin()
{
    $path = '/login';
    if ($this->identification->isUserLoggedIn()) {
        $path = '/dashboard';
    }
    return new RedirectResponse($path); 
}

Trong trường hợp này, khung nhìn tạo ra một trong hai phản hồi có thể, dựa trên trạng thái hiện tại của lớp mô hình. Đối với trường hợp sử dụng khác, bạn sẽ có chế độ xem chọn các mẫu khác nhau để kết xuất, dựa trên thứ gì đó như "bài viết được chọn hiện tại".

Lớp trình bày thực sự có thể trở nên khá phức tạp, như được mô tả ở đây: Hiểu về khung nhìn MVC trong PHP .

Nhưng tôi chỉ đang tạo một API REST!

Tất nhiên, có những tình huống, khi đây là một quá mức cần thiết.

MVC chỉ là một giải pháp cụ thể cho nguyên tắc Tách biệt các mối quan tâm . MVC tách giao diện người dùng khỏi logic nghiệp vụ và trong giao diện người dùng, nó phân tách xử lý đầu vào của người dùng và bản trình bày. Điều này là rất quan trọng. Mặc dù mọi người thường mô tả nó như là một "bộ ba", nhưng nó không thực sự được tạo thành từ ba phần độc lập. Cấu trúc giống như thế này:

Phân tách MVC

Điều đó có nghĩa là, khi logic của lớp trình bày của bạn gần với không tồn tại, cách tiếp cận thực tế là giữ chúng dưới dạng một lớp. Nó cũng có thể đơn giản hóa một số khía cạnh của lớp mô hình.

Sử dụng phương pháp này, ví dụ đăng nhập (đối với API) có thể được viết là:

public function postLogin(Request $request)
{
    $email = $request->get('email');
    $data = [
        'status' => 'ok',
    ];
    try {
        $identity = $this->identification->findIdentityByEmailAddress($email);
        $token = $this->identification->loginWithPassword(
            $identity,
            $request->get('password')
        );
    } catch (FailedIdentification $exception) {
        $data = [
            'status' => 'error',
            'message' => 'Login failed!',
        ]
    }

    return new JsonResponse($data);
}

Mặc dù điều này không bền vững, khi bạn có logic phức tạp để hiển thị phần thân phản hồi, việc đơn giản hóa này rất hữu ích cho các tình huống tầm thường hơn. Nhưng được cảnh báo , cách tiếp cận này sẽ trở thành một cơn ác mộng, khi cố gắng sử dụng trong các cơ sở mã lớn với logic trình bày phức tạp.

 

Làm thế nào để xây dựng mô hình?

Vì không có một lớp "Model" nào (như đã giải thích ở trên), nên bạn thực sự không "xây dựng mô hình". Thay vào đó, bạn bắt đầu từ việc tạo Dịch vụ , có thể thực hiện một số phương pháp nhất định. Và sau đó thực hiện các đối tượng miềnMappers .

Một ví dụ về phương thức dịch vụ:

Trong cả hai cách tiếp cận ở trên đều có phương thức đăng nhập này cho dịch vụ nhận dạng. Nó thực sự sẽ trông như thế nào. Tôi đang sử dụng một phiên bản sửa đổi một chút của cùng chức năng từ một thư viện mà tôi đã viết .. vì tôi lười biếng:

public function loginWithPassword(Identity $identity, string $password): string
{
    if ($identity->matchPassword($password) === false) {
        $this->logWrongPasswordNotice($identity, [
            'email' => $identity->getEmailAddress(),
            'key' => $password, // this is the wrong password
        ]);

        throw new PasswordMismatch;
    }

    $identity->setPassword($password);
    $this->updateIdentityOnUse($identity);
    $cookie = $this->createCookieIdentity($identity);

    $this->logger->info('login successful', [
        'input' => [
            'email' => $identity->getEmailAddress(),
        ],
        'user' => [
            'account' => $identity->getAccountId(),
            'identity' => $identity->getId(),
        ],
    ]);

    return $cookie->getToken();
}

Như bạn có thể thấy, ở mức độ trừu tượng này, không có dấu hiệu cho thấy dữ liệu được lấy từ đâu. Nó có thể là một cơ sở dữ liệu, nhưng nó cũng có thể chỉ là một đối tượng giả cho mục đích thử nghiệm. Ngay cả các trình ánh xạ dữ liệu, thực sự được sử dụng cho nó, cũng bị ẩn đi trong các privatephương thức của dịch vụ này.

private function changeIdentityStatus(Entity\Identity $identity, int $status)
{
    $identity->setStatus($status);
    $identity->setLastUsed(time());
    $mapper = $this->mapperFactory->create(Mapper\Identity::class);
    $mapper->store($identity);
}

Cách tạo ánh xạ

Để thực hiện một sự trừu tượng của sự kiên trì, trên các phương pháp linh hoạt nhất là tạo ra các trình ánh xạ dữ liệu tùy chỉnh .

Sơ đồ

Từ: sách PoEAA

Trong thực tế, chúng được thực hiện để tương tác với các lớp hoặc siêu lớp cụ thể. Hãy nói rằng bạn có CustomerAdmintrong mã của bạn (cả hai đều thừa hưởng từ một Usersiêu lớp). Cả hai có thể sẽ có một trình ánh xạ phù hợp riêng biệt, vì chúng chứa các trường khác nhau. Nhưng bạn cũng sẽ kết thúc với các hoạt động được chia sẻ và thường được sử dụng. Ví dụ: cập nhật thời gian "nhìn thấy lần cuối trực tuyến" . Và thay vì làm cho các trình ánh xạ hiện tại trở nên phức tạp hơn, cách tiếp cận thực tế hơn là có một "Trình ánh xạ người dùng" chung, chỉ cập nhật dấu thời gian đó.

Một số ý kiến ​​bổ sung:

  1. Bảng và mô hình cơ sở dữ liệu

    Mặc dù đôi khi có mối quan hệ 1: 1: 1 trực tiếp giữa bảng cơ sở dữ liệu, Đối tượng miềnMapper , trong các dự án lớn hơn, nó có thể ít phổ biến hơn bạn mong đợi:

    • Thông tin được sử dụng bởi một Đối tượng Miền duy nhất có thể được ánh xạ từ các bảng khác nhau, trong khi bản thân đối tượng đó không có sự tồn tại trong cơ sở dữ liệu.

      Ví dụ: nếu bạn đang tạo báo cáo hàng tháng. Điều này sẽ thu thập thông tin từ các bảng khác nhau, nhưng không có MonthlyReportbảng phép thuật trong cơ sở dữ liệu.

    • Một Mapper duy nhất có thể ảnh hưởng đến nhiều bảng.

      Ví dụ: khi bạn đang lưu trữ dữ liệu từ Userđối tượng, Đối tượng miền này có thể chứa bộ sưu tập các đối tượng miền khác - Groupphiên bản. Nếu bạn thay đổi chúng và lưu trữ User, Data Mapper sẽ phải cập nhật và / hoặc chèn các mục trong nhiều bảng.

    • Dữ liệu từ một Đối tượng Miền duy nhất được lưu trữ trong nhiều bảng.

      Ví dụ: trong các hệ thống lớn (nghĩ: mạng xã hội cỡ trung bình), có thể thực tế khi lưu trữ dữ liệu xác thực người dùng và dữ liệu thường được truy cập tách biệt khỏi khối nội dung lớn hơn, điều này hiếm khi được yêu cầu. Trong trường hợp đó, bạn vẫn có thể có một Userlớp duy nhất , nhưng thông tin chứa trong đó sẽ phụ thuộc vào việc liệu các chi tiết đầy đủ có được tìm nạp hay không.

    • Đối với mỗi Đối tượng miền, có thể có nhiều hơn một trình ánh xạ

      Ví dụ: bạn có một trang web tin tức với một mã được chia sẻ cho cả phần mềm quản lý và công khai. Nhưng, trong khi cả hai giao diện sử dụng cùng một Articlelớp, quản lý cần nhiều thông tin hơn trong đó. Trong trường hợp này, bạn sẽ có hai trình ánh xạ riêng biệt: "nội bộ" và "bên ngoài". Mỗi thực hiện các truy vấn khác nhau, hoặc thậm chí sử dụng các cơ sở dữ liệu khác nhau (như trong chủ hoặc nô lệ).

  2. Một khung nhìn không phải là một mẫu

    Xem các phiên bản trong MVC (nếu bạn không sử dụng biến thể MVP của mẫu) chịu trách nhiệm về logic trình bày. Điều này có nghĩa là mỗi Chế độ xem thường sẽ tung hứng ít nhất một vài mẫu. Nó lấy dữ liệu từ Lớp mẫu và sau đó, dựa trên thông tin nhận được, chọn một mẫu và đặt các giá trị.

    Một trong những lợi ích bạn có được từ việc này là khả năng sử dụng lại. Nếu bạn tạo một ListViewlớp, sau đó, với mã được viết tốt, bạn có thể có cùng một lớp bàn giao việc trình bày danh sách người dùng và nhận xét bên dưới một bài viết. Bởi vì cả hai đều có logic trình bày giống nhau. Bạn chỉ cần chuyển mẫu.

    Bạn có thể sử dụng các mẫu PHP gốc hoặc sử dụng một số công cụ tạo khuôn mẫu của bên thứ ba. Cũng có thể có một số thư viện của bên thứ ba, có thể thay thế hoàn toàn các phiên bản View .

  3. Còn phiên bản cũ của câu trả lời thì sao?

    Thay đổi lớn duy nhất là, cái được gọi là Model trong phiên bản cũ, thực sự là một Dịch vụ . Phần còn lại của "thư viện tương tự" theo kịp khá tốt.

    Lỗ hổng duy nhất mà tôi thấy là đây sẽ là một thư viện thực sự kỳ lạ, bởi vì nó sẽ trả về cho bạn thông tin từ cuốn sách, nhưng không cho phép bạn chạm vào cuốn sách, bởi vì nếu không thì sự trừu tượng sẽ bắt đầu "rò rỉ". Tôi có thể phải nghĩ về một sự tương tự phù hợp hơn.

  4. Mối quan hệ giữa các phiên bản View và Trình điều khiển là gì?

    Cấu trúc MVC bao gồm hai lớp: ui và model. Các cấu trúc chính trong lớp UI là khung nhìn và bộ điều khiển.

    Khi bạn đang làm việc với các trang web sử dụng mẫu thiết kế MVC, cách tốt nhất là có mối quan hệ 1: 1 giữa các khung nhìn và bộ điều khiển. Mỗi chế độ xem đại diện cho toàn bộ một trang trong trang web của bạn và nó có một bộ điều khiển chuyên dụng để xử lý tất cả các yêu cầu đến cho chế độ xem cụ thể đó.

    Ví dụ, để đại diện cho một bài viết đã mở, bạn sẽ có \Application\Controller\Document\Application\View\Document. Điều này sẽ chứa tất cả các chức năng chính cho lớp UI, khi nói đến việc xử lý các bài viết (tất nhiên bạn có thể có một số thành phần XHR không liên quan trực tiếp đến bài viết) .


4
@Rinzler, bạn sẽ nhận thấy rằng không nơi nào trong liên kết đó, bất cứ điều gì nói về Mô hình (ngoại trừ trong một nhận xét). Nó chỉ là "một giao diện hướng đối tượng đến các bảng cơ sở dữ liệu" . Nếu bạn cố gắng tạo ra điều này trong một điều giống như Mô hình, bạn sẽ vi phạm SRPLSP .
tereško

8
@hafichuk chỉ tình huống, khi sử dụng mẫu ActiveRecord là hợp lý để tạo mẫu. Khi bạn bắt đầu viết mã có ý nghĩa cho sản xuất, nó sẽ trở thành một mô hình chống, bởi vì nó pha trộn lưu trữ và logic kinh doanh. Và kể từ khi mẫu lớp là hoàn toàn không biết gì về những phần MVC khác. Điều này không thay đổi tùy thuộc vào sự thay đổi trên mẫu ban đầu . Ngay cả khi sử dụng MVVM. Không có "nhiều mô hình" và chúng không được ánh xạ tới bất cứ thứ gì. Mô hình là một lớp.
tereško

3
Phiên bản ngắn - Mô hình là cấu trúc dữ liệu .
Eddie B

9
Cũng thấy rằng ông đã phát minh ra MVC bài viết có thể có một số giá trị.
Eddie B

3
... Hoặc thậm chí chỉ là một bộ chức năng. MVC không yêu cầu phải được thực hiện theo kiểu OOP, mặc dù nó chủ yếu được thực hiện theo cách đó. Điều quan trọng nhất là tách các lớp và thiết lập luồng dữ liệu và kiểm soát phù hợp
hek2mgl

37

Mọi thứ thuộc về logic nghiệp vụ đều thuộc về một mô hình, cho dù đó là truy vấn cơ sở dữ liệu, tính toán, cuộc gọi REST, v.v.

Bạn có thể có quyền truy cập dữ liệu trong chính mô hình, mẫu MVC không hạn chế bạn làm điều đó. Bạn có thể đường áo nó với các dịch vụ, người vẽ bản đồ và những gì không, nhưng định nghĩa thực tế của mô hình là một lớp rằng logic xử lý kinh doanh, không có gì hơn, không kém. Nó có thể là một lớp học, một chức năng, hoặc một module hoàn chỉnh với một đối tượng gazillion nếu đó là những gì bạn muốn.

Luôn luôn dễ dàng hơn để có một đối tượng riêng thực sự thực hiện các truy vấn cơ sở dữ liệu thay vì thực hiện chúng trực tiếp trong mô hình: điều này đặc biệt hữu ích khi kiểm tra đơn vị (vì dễ dàng tiêm một phụ thuộc cơ sở dữ liệu giả vào mô hình của bạn):

class Database {
   protected $_conn;

   public function __construct($connection) {
       $this->_conn = $connection;
   }

   public function ExecuteObject($sql, $data) {
       // stuff
   }
}

abstract class Model {
   protected $_db;

   public function __construct(Database $db) {
       $this->_db = $db;
   }
}

class User extends Model {
   public function CheckUsername($username) {
       // ...
       $sql = "SELECT Username FROM" . $this->usersTableName . " WHERE ...";
       return $this->_db->ExecuteObject($sql, $data);
   }
}

$db = new Database($conn);
$model = new User($db);
$model->CheckUsername('foo');

Ngoài ra, trong PHP, bạn hiếm khi cần bắt / rút lại các ngoại lệ vì backtrace được giữ nguyên, đặc biệt trong trường hợp như ví dụ của bạn. Chỉ cần để ngoại lệ được ném và bắt nó trong bộ điều khiển thay thế.


Cấu trúc của tôi rất giống nhau, tôi nghĩ rằng tôi chỉ tách nó ra một chút nữa. Lý do tại sao tôi chuyển qua kết nối là vì tôi cần phải có các khối chạy trong các giao dịch. Tôi muốn thêm một người dùng và sau đó thêm người dùng vào một vai trò, nhưng vai trò trở lại nếu một người thất bại. Cách duy nhất tôi có thể sắp xếp nó là vượt qua kết nối.
Dietpixel

10
-1: nó cũng xảy ra là hoàn toàn sai. Mô hình không phải là một sự trừu tượng cho một bảng.
tereško

1
Các Userlớp cơ bản mở rộng các mô hình, nhưng itsn't một đối tượng. Người dùng phải là một đối tượng và có các thuộc tính như: id, name ... Bạn đang triển khai Userlớp là một người trợ giúp.
TomSawyer

1
Tôi nghĩ bạn hiểu MVC nhưng không hiểu OOP là gì. Trong kịch bản này, như tôi đã nói, Userlà viết tắt của một đối tượng và nó phải có các thuộc tính của Người dùng, không phải các phương thức như CheckUsername, bạn nên làm gì nếu muốn tạo Userđối tượng mới ? new User($db)
TomSawyer

@TomSawyer OOP không có nghĩa là các đối tượng được yêu cầu phải có các thuộc tính. Những gì bạn đang mô tả là một mẫu thiết kế, một mẫu không liên quan đến câu hỏi hoặc câu trả lời cho câu hỏi đó. OOP là một mô hình ngôn ngữ, không phải là một mẫu thiết kế.
netcoder

20

Trong Web- "MVC", bạn có thể làm bất cứ điều gì bạn muốn.

Khái niệm ban đầu (1) mô tả mô hình là logic kinh doanh. Nó nên đại diện cho trạng thái ứng dụng và thực thi một số thống nhất dữ liệu. Cách tiếp cận đó thường được mô tả là "mô hình chất béo".

Hầu hết các khung công tác PHP tuân theo cách tiếp cận nông cạn hơn, trong đó mô hình chỉ là giao diện cơ sở dữ liệu. Nhưng ít nhất các mô hình này vẫn nên xác nhận dữ liệu và quan hệ đến.

Dù bằng cách nào, bạn sẽ không ở rất xa nếu bạn tách các công cụ SQL hoặc các cuộc gọi cơ sở dữ liệu sang một lớp khác. Bằng cách này, bạn chỉ cần quan tâm đến dữ liệu / hành vi thực tế, không phải với API lưu trữ thực tế. (Tuy nhiên, thật vô lý khi lạm dụng nó. Bạn sẽ không bao giờ có thể thay thế một phụ trợ cơ sở dữ liệu bằng một bộ lọc nếu điều đó không được thiết kế trước.)


8
liên kết không hợp lệ (404)
Kyslik


6

Thông thường, hầu hết các ứng dụng sẽ có dữ liệu, phần hiển thị và xử lý và chúng tôi chỉ cần đặt tất cả những thứ đó trong các chữ cái M, VC.

Model ( M) -> Có các thuộc tính giữ trạng thái ứng dụng và nó không biết bất kỳ điều gì về VC.

Xem ( V) -> Đã hiển thị định dạng cho ứng dụng và chỉ biết về mô hình cách tiêu hóa trên ứng dụng và không bận tâm về điều đó C.

Bộ điều khiển ( C) ----> Có phần xử lý của ứng dụng và hoạt động như nối dây giữa M và V và nó phụ thuộc vào cả hai M, Vkhông giống MV.

Hoàn toàn có sự tách biệt mối quan tâm giữa mỗi. Trong tương lai, bất kỳ thay đổi hoặc cải tiến có thể được thêm vào rất dễ dàng.


0

Trong trường hợp của tôi, tôi có một lớp cơ sở dữ liệu xử lý tất cả các tương tác cơ sở dữ liệu trực tiếp như truy vấn, tìm nạp, v.v. Vì vậy, nếu tôi phải thay đổi cơ sở dữ liệu của mình từ MySQL sang PostgreSQL thì sẽ không có vấn đề gì. Vì vậy, thêm lớp bổ sung có thể hữu ích.

Mỗi bảng có thể có lớp riêng và có các phương thức cụ thể, nhưng để thực sự có được dữ liệu, nó cho phép lớp cơ sở dữ liệu xử lý nó:

Tập tin Database.php

class Database {
    private static $connection;
    private static $current_query;
    ...

    public static function query($sql) {
        if (!self::$connection){
            self::open_connection();
        }
        self::$current_query = $sql;
        $result = mysql_query($sql,self::$connection);

        if (!$result){
            self::close_connection();
            // throw custom error
            // The query failed for some reason. here is query :: self::$current_query
            $error = new Error(2,"There is an Error in the query.\n<b>Query:</b>\n{$sql}\n");
            $error->handleError();
        }
        return $result;
    }
 ....

    public static function find_by_sql($sql){
        if (!is_string($sql))
            return false;

        $result_set = self::query($sql);
        $obj_arr = array();
        while ($row = self::fetch_array($result_set))
        {
            $obj_arr[] = self::instantiate($row);
        }
        return $obj_arr;
    }
}

Bảng đối tượng lớpL

class DomainPeer extends Database {

    public static function getDomainInfoList() {
        $sql = 'SELECT ';
        $sql .='d.`id`,';
        $sql .='d.`name`,';
        $sql .='d.`shortName`,';
        $sql .='d.`created_at`,';
        $sql .='d.`updated_at`,';
        $sql .='count(q.id) as queries ';
        $sql .='FROM `domains` d ';
        $sql .='LEFT JOIN queries q on q.domainId = d.id ';
        $sql .='GROUP BY d.id';
        return self::find_by_sql($sql);
    }

    ....
}

Tôi hy vọng ví dụ này giúp bạn tạo ra một cấu trúc tốt.


12
"Vì vậy, nếu tôi phải thay đổi cơ sở dữ liệu của mình từ MySQL sang PostgreSQL thì sẽ không có vấn đề gì." Uhhhmmm với mã ở trên, bạn sẽ có một vấn đề lớn khi thay đổi bất cứ điều gì imo.
PeeHaa

Tôi thấy câu trả lời của tôi ngày càng ít ý nghĩa hơn sau khi chỉnh sửa, và khi thời gian trôi qua. Nhưng nó nên ở lại đây
Ibu

2
Databasetrong ví dụ không phải là một lớp Nó chỉ là một trình bao bọc cho các chức năng. Ngoài ra, làm thế nào bạn có thể có "lớp đối tượng bảng" mà không có đối tượng?
tereško

2
@ tereško Tôi đã đọc nhiều bài viết của bạn và chúng rất tuyệt. Nhưng, tôi không thể tìm thấy bất kỳ khuôn khổ hoàn chỉnh nào ở bất cứ đâu để nghiên cứu. Bạn có biết cái nào "đúng không"? Hoặc ít nhất một cái mà nó thích bạn và một số người khác ở đây trên SO nói phải làm gì? Cảm ơn.
johnny

Tôi có thể đi muộn, nhưng tôi muốn chỉ ra rằng PDO gần như giải quyết được vấn đề phải tạo ra một 'lớp DB' để tạo điều kiện cho những thay đổi trong tương lai.
Matthew Goulart
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.