5

このトピックは繰り返し尋ねられ、対処されてきたことに気づきました。数え切れないほどの同様の質問を読み、数え切れないほどの記事を読みましたが、いくつかの重要な懸念事項をまだ把握できていません...私は自分のMVCを構築しようとしています学習目的のためのフレームワークであり、OOP をよりよく理解するためのものです。これは個人的な個人的な使用のためのものであり、怠惰の言い訳として意味するものではありませんが、より堅牢なフレームワークのすべての機能を備えていることにはあまり関心がありません。

私のディレクトリ構造は次のとおりです。

public
- index.php
private
- framework
  - controllers
  - models
  - views
  - FrontController.php
  - ModelFactory.php
  - Router.php
  - View.php
- bootstrap.php

すべてのリクエストを index.php に送信する .htaccess ファイルがあります。このファイルには、タイムゾーンやグローバル定数などの基本的な構成設定が含まれており、bootstrap.php ファイルをロードします。ブートストラップには、クラスのオートローダーが含まれており、セッションを開始し、プロジェクト全体で使用するグローバル関数を定義してから、ルーターを呼び出します。ルーターは URL からリクエストを分離し、ReflectionClass を使用して検証し、example.com/controller/method/params の形式でリクエストを実行します。

すべてのコントローラーは FrontController.php を拡張します。

<?php
namespace framework;

class FrontController
{
    public $model;
    public $view;
    public $data = [];

    function __construct()
    {
        $this->model = new ModelFactory();
        $this->view = new View();
    }

    // validate user input
    public function validate() {}

    // determines whether or not a form is being submitted
    public function formSubmit() {}

    // check $_SESSION for preserved input errors
    public function formError() {}
}

このフロント コントローラーは ModelFactory をロードします。

<?php
namespace framework;

class ModelFactory
{
    private $db       = null;
    private $host     = 'localhost';
    private $username = 'dev';
    private $password = '********';
    private $database = 'test';

    // connect to database
    public function connect() {}

    // instantiate a model with an optional database connection
    public function build($model, $database = false) {}
}

そしてベースビュー:

<?php
namespace framework;

class View
{
    public function load($view, array $data = [])
    {
        // calls sanitize method for output
        // loads header, view, and footer
    }

    // sanitize output
    public function sanitize($output) {}

    // outputs a success message or list of errors
    // returns an array of failed input fields
    public function formStatus() {}
}

最後に、リクエストが現在どのように処理されているかを示すコントローラーの例を次に示します。

<?php
namespace framework\controllers;

use framework\FrontController,
    framework\Router;

class IndexController extends FrontController implements InterfaceController
{
    public function contact()
    {
        // process form if submitted
        if ($this->formSubmit()) {
            // validate input
            $name = isset($_POST['name']) && $this->validate($_POST['name'], 'raw') ? $_POST['name'] : null;
            $email = isset($_POST['email']) && $this->validate($_POST['email'], 'email') ? $_POST['email'] : null;
            $comments = isset($_POST['comments']) && $this->validate($_POST['comments'], 'raw') ? $_POST['comments'] : null;

            // proceed if required fields were validated
            if (isset($name, $email, $comments)) {
                // send message
                $mail = $this->model->build('mail');
                $to = WEBMASTER;
                $from = $email;
                $subject = $_SERVER['SERVER_NAME'] . ' - Contact Form';
                $body = $comments . '<br /><br />' . "\r\n\r\n";
                $body .= '-' . $name;

                if ($mail->send($to, $from, $subject, $body)) {
                    // status update
                    $_SESSION['success'] = 'Your message was sent successfully.';
                }
            } else {
                // preserve input
                $_SESSION['preserve'] = $_POST;

                // highlight errors
                if (!isset($name)) {
                    $_SESSION['failed']['name'] = 'Please enter your name.';
                }
                if (!isset($email)) {
                    $_SESSION['failed']['email'] = 'Please enter a valid e-mail address.';
                }
                if (!isset($comments)) {
                    $_SESSION['failed']['comments'] = 'Please enter your comments.';
                }
            }
            Router::redirect('contact');
        }

        // check for preserved input
        $this->data = $this->formError();

        $this->view->load('contact', $this->data);
    }
}

私が理解できることから、私の論理は次の理由でオフになっています。

  • 検証は、コントローラーではなくモデルで行う必要があります。ただし、モデルは $_POST 変数にアクセスできないため、この部分を正しく行っているかどうかは完全にはわかりません。これは彼らが「太ったコントローラー」と呼んでいるものだと思いますが、これは悪いことですが、何を変更する必要があるのか​​ わかりません...
  • コントローラーはビューにデータを送信するべきではありません。代わりに、ビューは独自のデータを要求するためにモデルにアクセスできる必要があります。$dataプロパティを FrontController から ModelFactory に移動し、データを渡さずに Controller から View を呼び出すと、この問題は解決しますか? 技術的には、MVC フローチャートに準拠しますが、提案されたソリューションは、それほど単純であると仮定すると、取るに足らない、または些細なことのように見えますが、おそらくそうではありません..
  • 実装全体に疑問を抱く部分は、ユーザーに対応するロールと権限でインスタンス化された User オブジェクトがあり、isAllowed()呼び出すことができるメソッドを作成する方法またはより具体的にはどこにあるかを理解しようとしているということですコントローラーとビューの両方。コントローラーとビューの両方がモデルにアクセスできる必要があるため、このメソッドをモデルに配置することは理にかなっていますか?

全体として、私はここで正しい道を進んでいますか、それとも正しい道を歩むために対処する必要がある明白な問題は何ですか? 「これを読んでください」ではなく、私の例に固有の個人的な応答を本当に望んでいます..正直なフィードバックと助けに感謝します。

4

3 に答える 3

1

まず、独自のフレームワークを作成しようとしているのは素晴らしいことだと思います。多くの人は、学習目的だけであれば、誰もがこれを行うべきだと言います。

次に、フレームワークに関するこのウィキペディアの記事を読むことをお勧めします。多くの人は、ルーティング (URL ディスパッチ、トラバーサル) とビュー (プッシュ、プル) にさまざまなパターンがあることに気づいていません。

個人的には、スーパー グローバルは生の入力 (php://input) から (php によって) 既に抽象化されており、変更できるため、抽象化する必要はないと思います。私の意見です。

モデルによって検証が行われるべきであることは正しいです。フォームを検証するのではなく、データを検証します。データにアクセスするビューに関しては、選択したパターンによって異なります。ビューにデータをプッシュすることも、ビューがデータをプルすることもできます。

興味があれば、MVC の基本的なフレームワークでの私の試みは github にあります。その 4 つのファイル、少ない 2K 行のコード (DB レイヤーは 1K 行)。トラバーサル (コンポーネント) ルーティングを実装し、データをプルします。代替パターンを実装するフレームワークはすでにたくさんあります。

于 2013-05-21T01:36:23.760 に答える
1
  • この投稿$_POSTで説明されているように、スーパーグローバルはリクエスト インスタンスによって抽象化する必要があります。

  • 入力の検証は、コントローラーの責任ではありません。代わりに、モデル レイヤー内のドメイン オブジェクトによって処理される必要があります。

  • モデル工場はモデルではありません。

  • publicクラス パラメーターの可視性をオブジェクトのカプセル化を破るものとして定義します。

  • HTTP ロケーション ヘッダー (リダイレクト) は、応答の形式です。したがって、ビュー インスタンスで処理する必要があります。

  • 現在の形式では、コントローラーはスーパーグローバルを直接操作しています。これにより、グローバル状態への密結合が発生します。

  • 認可チェックはcontroller の外部で実行する必要があります。その中にはありません。

  • 「モデル ファクトリ」は代わりに、コントローラとビューの両方に挿入されるサービス ファクトリにする必要があります。これにより、各サービスが 1 回だけインスタンス化されるため、コントローラーが同じモデル レイヤーの状態で動作できるようになります。

于 2013-05-20T22:53:26.800 に答える
0

検証は、コントローラーではなくモデルで行う必要があります。ただし、モデルは $_POST 変数にアクセスできないため、この部分を正しく行っているかどうかは完全にはわかりません。これは彼らが「太ったコントローラー」と呼んでいるものだと思いますが、これは悪いことですが、何を変更する必要があるのか​​ わかりません...

そうです、モデルはリクエストについて何も知らないはずなので、$_POST をモデルに渡す必要がありますが、それがリクエスト パラメータであることはわかりません。

1 つ: ビジネス ロジックとは関係のない検証は、コントローラーにとどめておく必要があります。セキュリティ上の理由から、フォームの CSRF トークンを作成するとします。このバリタディオンは、リクエストを処理するため、コントローラー内にある必要があります。

コントローラーはビューにデータを送信するべきではありません。代わりに、ビューは独自のデータを要求するためにモデルにアクセスできる必要があります。$data プロパティを FrontController から ModelFactory に移動し、データを渡さずに Controller から View を呼び出すと、この問題は解決しますか? 技術的には、MVC フローチャートに準拠しますが、提案されたソリューションは、それほど単純であると仮定すると、取るに足らない、または些細なことのように見えますが、おそらくそうではありません..

それは必ずしも真実ではありません。このアプローチはActive Modelと呼ばれ、通常はObserverパターンを使用し、モデルはビューによって観察されます。一部のモデルが変更された場合、ビュー自体が更新されることを通知します。このアプローチは、Web ベースのアプリケーションではなく、デスクトップ アプリケーションにより適しています。Web アプリケーションでは、最も一般的なのは、モデルとビュー (パッシブ モデル) の間の仲介者としてコントローラーを使用することです。正しいアプローチはありません。最も好きなものを選択する必要があります。

実装全体に疑問を抱く部分は、ユーザーに対応するロールと権限でインスタンス化された User オブジェクトがあることです。コントローラーとビューの両方から呼び出されます。コントローラーとビューの両方がモデルにアクセスできる必要があるため、このメソッドをモデルに配置することは理にかなっていますか?

まあ、これは仕方がありません。ACLについて読むように言わなければなりません。

于 2013-05-20T22:28:27.253 に答える