Получение параметров маршрутизатора с использованием шаблона реестра

У меня есть этот 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]);
}
}

Базовый / основной контроллер дополнительно имеет $ registry в своей функции конструкции.

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 это то, что отображается

введите описание изображения здесь

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

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

Неустранимая ошибка: вызов функции-члена get () для массива в

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

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

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

1

Решение

Как 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 строк кода.

Постскриптум использование Объявления типа чтобы избежать подобных ошибок. Если ты пишешь public function __construct(Registry $registry) PHP бросает TypeError исключение при передаче массива.

4

Другие решения

Код, который вы разместили здесь, не может быть протестирован, так как в нем нет HomeController определение класса и тоже не совсем понятно где и в какой момент var_dump(...) называется. Но я попытался угадать вашу проблему на основе фатальной ошибки, которую вы упомянули, и позвонил var_dump() в edit() функция в вашем Posts учебный класс. Похоже, вы пытались сбросить $this->router->getParams() из этой функции.

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

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

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

2

По вопросам рекламы [email protected]