2017-02-07 4 views
1

bekommen Ich habe diese Router.php im Kern meiner Anwendung.Router-Parameter mit dem Registry-Muster

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; 
    } 
} 

Um einen zentralen Speicher für Objekte zu implementieren, habe ich dieses Registrierungsmuster implementiert, die den Kern der Struktur ist.

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]); 
    } 
} 

Die Basis/Kern-Controller ferner aufweist $ Register an seinem Konstrukt Funktion.

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); 
    } 
} 

Die CoreController von allen App-Controller erweitert wird, um die Eigenschaften zu erben.

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>'; 
    } 
} 

Um die Registrierung und Router instanziiert das ist das, was in der

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']); 

Nach der URL http://localhost/mvcsix/posts/1235/edit diese ist was angezeigt wird

enter image description here

All dies sieht gut aus und funktioniert gut.

Irgendwie fühlt sich das nicht richtig an. Ich habe var_dumped this-> Registrierung und ich habe die Parameter der Route angezeigt, aber ich fühle, dass, um die Parameter von der Route zu erhalten, ich hätte var_dumped $ this-> router-> getParams(). Als ich var_dump $ this-> Router-> getParams(), erhalte ich einen Fehler, der sagt

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

ich das sagen, weil ich das Datenbankobjekt in der Registrierung zu und die Abfrage zu erhalten, ich $result = $this->db->query("SELECT * FROM members"); tun anzuzeigen

Warum werden die Parameter auf $ this-> registry angezeigt und nicht auf $ this-> router-> getParams(); ?

P.S. Der obige Code ist ein Stripdown des ursprünglichen Codes. Es gibt Namespaces und einige weitere Dinge, die für diesen Beitrag nicht notwendig waren.

+0

Wie instanziieren Sie 'Posts' oder' CoreController'? Der Fehler sieht so aus, als ob Sie es als '$ controller = new Posts ([]) 'machen; und daher ist' registry 'ein Array. –

Antwort

4

Als alex_edev bemerkt, versuchen Sie get Methode auf einem Array aufrufen. Aber woher kommt es?

Was ist los?

Posts Controller wird in der Router-Methode dispatch initialisiert. Die URL /posts/1235/edit funktioniert die zweite Route Regel übereinstimmen, so werden die folgenden Zeilen

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

Acht ausgeführt, was an den Controller Konstruktor übergeben wird. Sie passieren die Route params Eigentum! Betrachtet man Posts.php, Posts Controller erweitert CoreController, so erwartet Registry als Parameter eines Konstruktors, aber Sie übergeben ein Array - Route::params Eigenschaft. Es ist also die falsche Objektkonstruktion, die die Party bremst.

Warum funktioniert es normal normal?

Alles funktioniert gut ohne var_dump seit Sie nicht zu Posts::__get Methode aufrufen. Wenn Sie $this->router->getParams() in Posts Controller aufrufen, versucht es, undefined router Eigenschaft mit Getter zu erhalten und aufgrund falscher Registrierung fehlschlägt - denken Sie daran, Sie ein Array in den Controller injiziert.

Was sollte

Sie sollten initiieren Controller auf diese Weise

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

wo registry injiziert wird im __construct erfolgen:

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

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

Der Router initiiert wird wie folgt

$registry->set('db', $db); 


$router = new \system\core\Router($registry); 

Also, Sie müssen nur 6 Zeilen Code bearbeiten.

P.S. Verwenden Sie Type declarations, um diese Art von Fehlern zu vermeiden. Wenn Sie schreiben public function __construct(Registry $registry) php löst eine TypeError Ausnahme, wenn Array übergeben wird.

+0

danke für den Hinweis so deutlich, Antwort akzeptiert Kumpel. –

2

Der Code, den Sie hier gepostet haben, kann nicht getestet werden, weil HomeController Klassendefinition fehlt und es auch nicht ganz klar ist, wo und zu welchem ​​Zeitpunkt var_dump(...) aufgerufen wird. Aber ich habe versucht, Ihr Problem basierend auf dem schwerwiegenden Fehler, den Sie erwähnt haben, zu erraten und rufen Sie var_dump() in edit() Funktion in Ihrer Posts Klasse. Es sieht so aus, als ob Sie versucht haben, $this->router->getParams() von dieser Funktion zu trennen.

"Schwerwiegender Fehler: Aufruf einer Memberfunktion get() für Array in" bedeutet, dass Sie versucht haben, $arr->get() unter $arr aufzurufen, das ein Array (kein Objekt) war. Sie haben einen Aufruf an eine solche get()-Funktion im Klassen-Getter Ihres CoreControllers. Und dieser Aufruf erfolgt aus dem Gültigkeitsbereich der $registry-Eigenschaft, die also einen Objekttyp haben sollte.

So sieht es aus wie in dieser Situation sollten Sie den Typ Ihres protected $registry überprüfen, bevor Sie versuchen, Dump $this->router->getParams(). Es wäre vielleicht nicht das, was Sie erwartet haben.

Ich habe nicht gefunden, wo Sie ein Objekt Posts Klasse in Ihrem Code instanziieren und was Sie als $registry in es __constructor() setzen, so konnte ich nicht meine Schätzung überprüfen. Wenn Sie das klären, wäre es einfacher, das Problem zu finden.