2017-02-07 14 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>'; 
    } 
} 

Чтобы создать экземпляр реестра и маршрутизатор это то, что находится в

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

После URL http://localhost/mvcsix/posts/1235/edit это это то, что отображается

enter image description here

Все это выглядит хорошо и отлично работает.

Как-то это не так. Я var_dumped $ this-> registry, и у меня есть параметры отображаемого маршрута, но я чувствую, что для получения параметров с маршрута я должен был бы var_dumped $ this-> router-> getParams(). Когда я var_dump $ this-> router-> getParams(), я получаю ошибку, которая говорит

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

Я говорю это потому, что у меня есть объект базы данных в реестре также и получить запрос для отображения я $result = $this->db->query("SELECT * FROM members");

Почему у меня есть параметры, отображаемые в $ this-> registry, а не на $ this-> router-> getParams(); ?

P.S. приведенный выше код является полосой исходного кода. Есть пространства имен и еще несколько вещей, которые не были необходимы для этого сообщения.

+0

Как вы создаете экземпляр 'Posts' или' CoreController'? Ошибка выглядит так: '$ controller = new Posts ([]) ';' и поэтому 'registry' - это массив. –

ответ

4

Как заметил alex_edev, вы пытаетесь вызвать метод get на массиве. Но откуда оно взялось?

Что не так.

Posts контроллер инициализируется способом маршрутизатора dispatch. URL /posts/1235/edit делает матч второго правила маршрута, поэтому следующие строки выполняются

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

Обратите внимание на то, что передается в конструктор контроллера. Вы проходите маршрут params собственности! Глядя на Posts.php, Posts, контроллер расширяет CoreController, поэтому он ожидает Registry в качестве параметра конструктора, но вы передаете массив - Route::params. Так что это неправильная конструкция объекта, которая тормозит партию.

Почему это нормально работает нормально.

Все работает нормально без var_dump, так как вы не звоните по номеру Posts::__get. Когда вы вызываете $this->router->getParams() в контроллере Posts, он пытается получить не определено router свойство с геттером и не удается из-за неправильного реестра - помните, вы ввели массив контроллеру.

Что должно быть сделано

Вы должны инициировать контроллер таким образом

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

где registry впрыскивается в __construct:

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

    // pass it to the router 
    public function __construct($registry) { 
     $this->registry = $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(...). Но я попытался угадать вашу проблему на основании фатальной ошибки, о которой вы упоминали, и позвоните по номеру var_dump() в функции edit() в вашем классе Posts. Похоже, вы попытались сбросить $this->router->getParams() из этой функции.

«Неустранимая ошибка: вызов функции-члена get() для массива в« означает, что вы пытались вызвать $arr->get() на $arr, который был массивом (а не объектом). У вас есть вызов такой функции get() в вашем геттере класса CoreController. И этот вызов выполняется из области $registry, поэтому должен иметь тип объекта.

Таким образом, похоже, что в этой ситуации вы должны проверить тип своего protected $registry перед тем, как попробовать дамп $this->router->getParams(). Возможно, это было не то, чего вы ожидали.

Я не нашел, где вы создаете объект класса Posts в своем коде и то, что вы положили как $registry в его __constructor(), так что я не мог проверить свою догадку. Если вы поясните это, было бы легче найти проблему.