2017-02-07 1 views
1

私はこのアプリケーションのコアにこのRouter.phpを持っています。レジストリパターンを使用してルータパラメータを取得する

Router.php

<?php 

final class Router 
{ 
    protected $routes = []; 
    protected $params = []; 

    public function add($route, $params = []) 
    { 
     $route = preg_replace('/\//', '\\/', $route); 
     $route = preg_replace('/\{([a-z]+)\}/', '(?P<\1>[a-z-]+)', $route); 
     $route = preg_replace('/\{([a-z]+):([^\}]+)\}/', '(?P<\1>\2)', $route); 
     $route = '/^' . $route . '$/i'; 

     $this->routes[$route] = $params; 
    } 

    public function getRoutes() 
    { 
     return $this->routes; 
    } 

    public function match($url) 
    { 
     foreach ($this->routes as $route => $params) { 
      if (preg_match($route, $url, $matches)) { 
       foreach ($matches as $key => $match) { 
        if (is_string($key)) { 
         $params[$key] = $match; 
        } 
       } 

       $this->params = $params; 
       return true; 
      } 
     } 

     return false; 
    } 

    public function getParams() 
    { 
     return $this->params; 
    } 

    public function dispatch($url) 
    { 
     $url = $this->removeQueryStringVariables($url); 

     if ($this->match($url)) { 
      $controller = $this->params['controller']; 
      $controller = $this->convertToStudlyCaps($controller); 
      $controller = $this->getNamespace() . $controller; 

      if (class_exists($controller)) { 
       $controller_object = new $controller($this->params); 
       $action = $this->params['action']; 
       $action = $this->convertToCamelCase($action); 

       if (is_callable([$controller_object, $action])) { 
        $controller_object->$action(); 

       } else { 
        echo "Method $action (in controller $controller) not found"; 
       } 
      } else { 
       echo "Controller class $controller not found"; 
      } 
     } else { 
      echo 'No route matched.'; 
     } 
    } 

    protected function convertToStudlyCaps($string) 
    { 
     return str_replace(' ', '', ucwords(str_replace('-', ' ', $string))); 
    } 

    protected function convertToCamelCase($string) 
    { 
     return lcfirst($this->convertToStudlyCaps($string)); 
    } 

    protected function removeQueryStringVariables($url) 
    { 
     if ($url != '') { 
      $parts = explode('&', $url, 2); 

      if (strpos($parts[0], '=') === false) { 
       $url = $parts[0]; 
      } else { 
       $url = ''; 
      } 
     } 

     return $url; 
    } 

    protected function getNamespace() 
    { 
     $namespace = 'catalog\controller\\'; 

     if (array_key_exists('namespace', $this->params)) { 
      $namespace .= $this->params['namespace'] . '\\'; 
     } 

     return $namespace; 
    } 
} 

オブジェクトの中央記憶装置を実現するために、私は、構造の中核であり、このレジストリパターンを実装しています。

Registry.php

<?php 
final class Registry 
{ 
    private $data = array(); 

    public function get($key) 
    { 
     return (isset($this->data[$key]) ? $this->data[$key] : null); 
    } 

    public function set($key, $value) 
    { 
     $this->data[$key] = $value; 
    } 

    public function has($key) 
    { 
     return isset($this->data[$key]); 
    } 
} 

さらにベース/コアコントローラは、その構築物の機能に$レジストリを有しています。

CoreController.php

<?php 
abstract class CoreController 
{ 
    protected $registry; 

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

    public function __get($key) 
    { 
     return $this->registry->get($key); 
    } 

    public function __set($key, $value) 
    { 
     $this->registry->set($key, $value); 
    } 
} 

CoreControllerプロパティを継承するすべてのアプリ制御部によって拡張されます。

Posts.php

<?php 
class Posts extends CoreController 
{ 
    public function index() { 
     echo 'Hello from the index action in the posts controller'; 
    } 

    public function addNew() { 
     echo 'Hello from the addNew action in the posts controller'; 
    } 

    public function edit() { 
     echo '<p>Route parameters: <pre>'.var_dump($this->registry).'</pre></p>'; 
    } 
} 

これは、レジストリやルータをインスタンス化するためにどのようなURL http://localhost/mvcsix/posts/1235/editこの後

のindex.php

<?php 
// Instantiate registry 
$registry = new \system\core\Registry(); 

// Database 
$db = new DB(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE); 
$registry->set('db', $db); 


$router = new \system\core\Router(); 
$registry->set('router', $router); 


// Add the routes 
$router->add('', ['controller'=>'HomeController', 'action'=>'index']); 
$router->add('posts', ['controller'=>'posts', 'action'=>'index']); 
//$router->add('posts/new', ['controller'=>'posts', 'action'=>'new']); 
$router->add('{controller}/{action}'); 
$router->add('{controller}/{id:\d+}/{action}'); 
$router->add('admin/{controller}/{action}'); 

$router->dispatch($_SERVER['QUERY_STRING']); 

であります表示されているものは

enter image description here

これはすべてうまく見えます。

どういうわけか、これは正しく感じられません。私はvar_dumped $ this-> registryと私は表示されているルートのパラメータを持っているが、私はvar_dumped $ this-> router-> getParams()を持っている必要があるルートからパラメータを取得すると感じる。私のvar_dumpます$ this-> router->のgetParams()、私はあまりにもレジストリにデータベースオブジェクトを持っていると私は$result = $this->db->query("SELECT * FROM members");を行う表示するクエリを取得するので、私はこれを言う

Fatal error: Call to a member function get() on array in

を言うエラーが出るときは

$ this-> router-> getParams()ではなく、パラメータを$ this-> registryに表示するのはなぜですか。 ?

P.S.上記のコードは、元のコードを削除しています。名前空間があり、この記事では必要でないものはほとんどありません。

+0

'Posts'や' CoreController'をどのようにインスタンス化しますか?エラーはあなたが '$ controller = new Posts([]) ';のように見えるので、' registry'は配列です。 –

答えて

4

通知されたalex_edevは、アレイ上でgetメソッドを呼び出そうとしています。しかし、それはどこから来ますか?

何が問題なのですか。

Postsコントローラはルータの方法dispatchで初期化されています。 URL /posts/1235/editは、第2の経路ルールに一致し、その次の行では、コントローラのコンストラクタに渡されたものに

$controller_object = new $controller($this->params); 
$action = $this->params['action']; 
$action = $this->convertToCamelCase($action); 

ご注意を実行しています。あなたは経路paramsプロパティを渡します! Posts.phpを見ると、コントローラーはCoreControllerになるので、Registryはコンストラクターのパラメーターとして期待されますが、配列にはRoute::paramsプロパティを渡します。だから、パーティーを制動するのは間違ったオブジェクト構成です。

なぜ正常に正常に動作しますか?

Posts::__getメソッドに電話していないので、すべてvar_dumpがなければ問題なく動作します。 をPostsコントローラに呼び出すと、ゲッターで未定義のrouterプロパティを取得しようとします。レジストリが正しくないために失敗します。コントローラにアレイを挿入したことを忘れないでください。

final class Router 
{ 
    // add definition 
    private $registry; 

    // pass it to the router 
    public function __construct($registry) { 
     $this->registry = $registry; 
    } 
    .... 
} 

を次のようにルータが開始されて:あなたはこのよう registry__constructに注入され

$controller_object = new $controller($this->registry); 

コントローラを開始すべきである何

を行うべきである

$registry->set('db', $db); $router = new \system\core\Router($registry); 

したがって、6行のコードを編集するだけで済みます。

P.S.この種のエラーを避けるには、Type declarationsを使用してください。 public function __construct(Registry $registry)を書くと、PHPが配列を渡したときに例外TypeErrorをスローします。

+0

非常にはっきりと指摘してくれてありがとう、答えられた仲間に答えてください。 –

2

ここに投稿したコードは、HomeControllerクラス定義が不足しているためテストできません。また、どこで、いつ瞬時にvar_dump(...)が呼び出されるのかは不明です。しかし、私はあなたの致命的なエラーに基づいてあなたの問題を推測しようとしたのedit()あなたのPostsクラスの関数で呼び出します。その機能から$this->router->getParams()をダンプしようとしたようです。

「致命的なエラー:中にアレイ上のメンバ関数のget()を呼び出し、」あなたはアレイ(ないオブジェクト)であった$arr$arr->get()を呼び出そうとしましたことを意味します。 CoreControllerのクラスゲッターにそのようなget()関数の呼び出しがあります。そしてその呼び出しは、$registryプロパティのスコープから行われ、Object型を持つ必要があります。

したがって、ダンプしようとする前にprotected $registryのタイプを確認する必要があります。$this->router->getParams()。それはあなたが期待していたものではないかもしれません。

コード内にPostsクラスのオブジェクトをインスタンス化する場所が見つかりませんでした。には__constructor()という名前を付けて、推測を確認できませんでした。これを明確にすれば、問題を見つけやすくなります。

関連する問題