6

この問題を解決する方法について、いくつかのヒントをお願いしたいと思います。独自の MVC Web サイトを構築しようとしています。URLの基本を学びました。

http://example.com/blog/cosplay/cosplayer-expo-today

ブログ -> コントローラの
コスプレ -> コントローラのメソッド
cosplayer-expo-today -> メソッドの変数

ブログ コントローラーでカテゴリを動的に拡張するとどうなりますか? メソッドを作成する必要がありますか、またはそれを自動的に行うためのトリックはありますか? つまり... 私は今これらのカテゴリを持っています: コスプレ、ゲーム、映画、シリーズ. したがって、コントローラーでこれらのメソッドを作成する必要がありますが、それらはすべて同じことを行います。つまり、データベースから他のカテゴリを選択します。

  • 関数 cosplay() = example.com/blog/cosplay/
  • 関数 game() = example.com/blog/game/
  • 関数 movie() = example.com/blog/movie/
  • 関数 series() = example.com/blog/series/

それを自動的に行うようにコントローラーを作成する方法について何か良いアドバイスはありますか? データベースに新しいカテゴリをアップロードしたとしても、コントローラーを変更したくありません。出来ますか?助けてくれてありがとう!

アップデート

これが私のURLエクスプローダークラスです

class Autoload
{
    var $url;
    var $controller;
    function __construct()
    {
        $this->url = $_GET['url'];
        //HNEM ÜRES AZ URL
        if($this->url!='' && !empty($this->url))
        {
            require 'application/config/routes.php';
            //URL VIZSGÁLATA
            $this->rewrite_url($this->url);

            //URL SZÉTBONTÁSA
            $this->url = explode('/', $this->url);

            $file = 'application/controllers/'.$this->url[0].'.php';
            //LÉTEZIK A CONTROLLER?
            if(file_exists($file))
            {
                require $file;
                $this->controller = new $this->url[0];

                //KÉRELEM ALATT VAN AZ ALOLDAL?
                if(isset($this->url[1]))
                {
                    //LÉTEZIK A METÓDUS? ENGEDÉLYEZVE VAN?
                    if(method_exists($this->controller, $this->url[1]) && in_array($this->url[1], $route[$this->url[0]]))
                    {
                        if(isset($this->url[2]))
                        {
                            $this->controller->{$this->url[1]}($this->url[2]);
                        }
                        else
                        {
                            $this->controller->{$this->url[1]}();
                        }
                    }
                    else
                    {
                        header('location:'.SITE.$this->url[0]);
                        die();
                    }
                }
            }
            else
            {
                header('location:'.SITE);
                die();
            }
        }
        else
        {
            header('location:'.SITE.'blog');
            die();
        }
    }

    /**
     * Első lépésben megvizsgáljuk, hogy a kapott szöveg tartalmaz-e nagybetűt. Amennyiben igen átalakítjuk kisbetűsre.<br/>
     * Második lépésben megnézzük, hogy a kapott szöveg '/'-re végződik-e. Amennyiben igen levágjuk azt.<br/>
     * Harmadik lépésben újra töltjük az oldalt a formázott szöveggel.
     * 
     * @param string $url Korábban beolvasott URL.
     */
    private function rewrite_url($url)
    {
        //HA NAGYBETŰ VAN AZ URL-BEN VAGY '/'-RE VÉGZŐDIK
        if(preg_match('/[A-Z]/', $url) || substr($url, -1)=='/')
        {
            //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK
            if(preg_match('/[A-Z]/', $url))
            {
                $url = strtolower($url);
            }
            //HA '/'-RE VÉGZŐDIK LEVÁGJUK
            if(substr($url, -1)=='/')
            {
                $url = substr($url, 0, strlen($url)-1);
            }
            header('location:'.SITE.$url);
            die();
        }
    }




}

そして、ここに私の.htaccessがあります

Options +FollowSymLinks
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d  
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
4

2 に答える 2

35

参考までに:あなたが間違っていることがいくつかあります。私はそれらのそれぞれを調べて、問題、誤解、および可能な解決策を説明しようとします.

自動ロードとルーティングは別のものです。

投稿したコードの外観から、次のタスクを担当する単一のクラスがあることは明らかです。

  • ルーティング: 残りのアプリケーションにとって重要な部分に URL を分割します。
  • autoloading: クラスは分離された URL セグメントを取得し、関連するコードを含めようとします
  • factory: 新しいインスタンスが初期化され、いくつかのメソッドが呼び出されます
  • 応答: 場合によっては、クラスは HTTP ヘッダーの形式でユーザーに応答を送信します

OOP には、 Single Responsibility Principle [ short version ]と呼ばれるものがあります。基本的には、クラスが特定のものを処理する必要があることを意味します。上記のリストは、クラスの少なくとも4 つの異なる責任を構成していますAutoload

現在持っているものの代わりに、これらの一般的なタスクのそれぞれを個別のクラスで処理する必要があります。また、オートローダーの場合は、1 つの機能で済む可能性があります。

独自のオートローディング コードを作成するにはどうすればよいですか?

私が見ている問題の一部は、PHP で autoload が実際にどのように機能するかについての混乱です。includeorの呼び出しはrequire、インスタンスが作成される場所で行う必要はありません。代わりに、(spl_autoload_register()関数を使用して) ハンドラーを登録します。これは、以前に未定義のクラスを使用しようとすると、**自動的に*呼び出されます。

最も簡単な例は次のとおりです。

spl_autoload_register( function( $name ) use ( $path ) {
    $filename = $path . '/' . $name . '.php';
    if ( file_exists( $filename ) === true ) {
        require $filename;
        return true;
    }
    return false;
});

この特定の例では、PHP 5.3 で導入された機能の 1 つである匿名関数spl_autoload_register()を使用していますが、のマニュアル ページには、オブジェクトまたは通常の関数で同じことを実現する方法の例も示されています。

自動読み込みに密接に関連するもう 1 つの新機能は、名前空間です。このコンテキストでは、名前空間は 2 つの直接的な利点をもたらします。同じ名前の複数のクラスを持つ機能と、複数のディレクトリからクラス ファイルをロードするオプションです。

たとえば、次のようなコードを作成できます。

$controller = new \Controllers\Overview;
$view = new \Views\Overview;

$controller->doSomething( $request );

.. この場合、/project/controllers/overview.phpおよび/project/views/overview.phpファイルからそれぞれクラスをフェッチするオートローダーを使用できます。はハンドラー関数に渡さspl_autoload_register()れるためです。"\Controllers\Overview""\Views\Overview"

オートローダの実装方法に関するFIGの推奨事項もあります。ここで見つけることができます。いくつかの重大な問題がありますが、構築するための優れた基盤を提供するはずです。

きれいな URL を解析する方法は?

周知のとおり、Apache のmod_rewriteはかなりのURLでできることがかなり制限されています。また、広く普及しているサーバーですが、Web サーバーの唯一のオプションではありません。これが、最大限の柔軟性を得るために、PHP 開発者が PHP 側で URL を処理することを選択する理由です。

そして、初心者が最初に行うことはexplode('/', ... ). これは当然の選択ですが、実際にできることも非常に限られていることにすぐに気付くでしょう。ルーティング メカニズムが成長し始めます。最初はセグメントの数に基づいて、後で - 異なる動作を必要とするセグメントに異なる条件値を追加します。

本質的に、これは巨大で壊れやすく、制御不能な混乱になります。悪いアイデア。

代わりに、指定されたきれいな URL と照合する正規表現のリストを用意する必要があります。例えば:

'#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#'

上記で定義されたパターンは、2 つのセグメントを持つすべての URL に一致し、最初のセグメントと"foobar"2 番目のセグメントにテキストが含まれます ... like "/testme/foobar".

さらに、各パターンを各一致の対応するデフォルト値にリンクできます。これをすべてまとめると、次のような構成になる可能性があります (5.4 以降の配列構文を使用します。これは、私が好きな書き方なので..対処するためです):

$routes = [
    'primary' => [
        'pattern'   => '#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#',
        'default'   => [
            'action'    => 'standard',
        ],
    ],
    'secundary' => [
        'pattern'   => '#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#',
        'default'   => [
            'resource'  => 'catalog',
            'action'    => 'view',
        ]
    ],
    'fallback'  => [
        'pattern'   => '#^.*$#',
        'default'   => [
            'resource'  => 'main',
            'action'    => 'landing',
        ],
    ],
]; 

次のコードを使用して処理できます。

// CHANGE THIS
$url = '/12345/product';

$current = null;

// matching the route
foreach ($routes as $name => $route) {
    $matches = [];
    if ( preg_match( $route['pattern'], $url, $matches ) ) {
        $current = $name;
        $matches = $matches + $route['default'];
        break;
    }
}


// cleaning up results
foreach ( array_keys($matches) as $key ) {
    if ( is_numeric($key) ) {
        unset( $matches[$key] );
    }
}


// view results
var_dump( $current, $matches );

Live code: here or here

注:
記法を使用する場合'(?P<name> .... )'、一致は'name'キーとして配列を返します。ルーティング以上の便利なトリック。

おそらく、より読みやすい表記法からマッチング用の正規表現を生成したいと思うでしょう。たとえば、構成ファイルでは、次の式:

'#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#'

..おそらく次のようになります

'/:id[[/:resource]/:action]'

:paramは URL セグメントを示し、URLの[...]オプション部分を示します。

これに基づいて、独自のルーティング システムを具体化できるはずです。上記のコード フラグメントは、単純化されたコア機能の単なる例です。完全に実装されたときにどのように見えるかについての見通しを得るには、この回答のコードを参照してください。独自の API バージョンについていくつかのアイデアが得られるはずです。

コントローラー上のものを呼び出す..

コントローラの実行をルーティング クラス (または複数のクラス) のどこか深いところに埋めてしまうのは、よくある間違いです。これにより、次の 2 つの問題が発生します。

  • 混乱: アプリケーションで「実際の作業」がどこから始まるかを見つけるのが難しくなります。
  • カップリング: ルーターは、MVC のようなアーキテクチャの特定の解釈にチェーンされてしまいます

ルーティングは、カスタム作成されたアプリケーションであっても、コードベースの「フレームワークっぽい」部分に自然に引き寄せられるタスクです。

(本当に)単純化されたバージョンは次のようになります。

$matches = $router->parse( $url );

$controller = new {'\\Controller\\'.$matches['controller']};
$controller->{$matches['action']( $matches );

このように、ルーティング結果を MVC のようなアーキテクチャで使用する必要はありません。たぶん、静的 HTML ファイルを提供するための美化されたフェッチ メカニズムが必要なだけかもしれません。

それらの動的に拡張されるカテゴリはどうですか?

あなたはそれを間違った方法で見ています。コントローラーにメソッドを動的に追加する必要はありません。あなたの例では、実際には1つのコントローラーメソッドがあります...次の行に沿ったものです:

public function getCategory( $request ) {
    $category = $request->getParameter('category');

    // ... rest of your controller method's code
}

、、、または追加したその他のカテゴリが$category含まれる場所。これは、記事を除外するためにコントローラーがモデル層に渡すものです。"cosplay""game""movie""series"

人々は実際にプロとして何を使用していますか?

最近では、誰もが (まあ.. なんらかの手掛かりを持っている人は) composerを使用しているため、オートロードには composer にバンドルされているローダーを使用するのが最適なオプションです。

追加するだけrequire __DIR__ . '/vendor/autoload.php'で、いくつかの構成で機能します。

ルーティングに関しては、 FastRouteまたはSymfony の Routing Componentという 2 つの主要な「スタンドアロン」ソリューションがあります。これらのものは、頭を悩ませることなくプロジェクトに含めることができます。

ただし、一部の人々はフレームワークを使用するため、それぞれにリクエストをルーティングする機能も含まれます。

さらに読む..

MVC アーキテクチャ パターンについて詳しく知りたい場合は、この投稿に記載されているすべての資料を参照することを強くお勧めします。必須の読書/視聴リストと考えてください。また、MVC 関連の件名に関する私の古い投稿 (ここここ、およびここ) が多少有益であることがわかるかもしれません。

PS: PHP 5.0 がリリースされてから (2004 年に)、クラスの変数は, の代わりにpublic,privateまたはを使用して定義する必要があります。protectedvar

于 2013-10-11T03:42:53.530 に答える