使用注册表模式获取路由器参数

时间:2017-02-07 11:26:20

标签: php model-view-controller registry router

我将此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被所有app控制器扩展为继承属性。

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

在网址http://localhost/mvcsix/posts/1235/edit后显示

enter image description here

这一切看起来都很好并且运作正常。

不知何故,这感觉不对。我var_dumped $ this-&gt;注册表,我有显示路由的参数,但我觉得要从路由获取参数我应该有var_dumped $ this-&gt; router-&gt; getParams()。当我var_dump $ this-&gt; router-&gt; getParams()时,我收到一条错误,上面写着

  

致命错误:在

中调用数组上的成员函数get()

我说这是因为我在注册表中也有数据库对象并且要显示查询我做$result = $this->db->query("SELECT * FROM members");

为什么我在$ this-&gt;注册表上显示参数而不在$ this-&gt; router-&gt; getParams(); ?

P.S。上面的代码是原始代码的删除。这个帖子还有一些名称空间和更多的东西。

2 个答案:

答案 0 :(得分:4)

当注意到alex_edev时,您正试图在数组上调用get方法。但它来自哪里?

出了什么问题。

Posts控制器在路由器的方法dispatch中初始化。网址/posts/1235/edit与第二条路线规则匹配,因此执行以下行

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

注意传递给控制器​​构造函数的内容。你传递路线params属性!查看Posts.phpPosts控制器扩展CoreController,因此它期望Registry作为构造函数的参数,但是您传递了一个数组 - Route::params属性。所以这是错误的对象结构,制动党。

为什么它能正常工作。

没有var_dump,一切正常,因为您没有调用Posts::__get方法。当您在$this->router->getParams()控制器中调用Posts时,它会尝试使用getter获取未定义的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异常。

答案 1 :(得分:2)

您在此处发布的代码无法进行测试,因为它缺少HomeController类定义,而且调用var_dump(...)的位置和时间也不太明确。但是我已经尝试根据您提到的致命错误猜测您的问题,并在var_dump()课程的edit()函数中致电Posts。看起来您试图从该函数转储$this->router->getParams()

&#34;致命错误:在&#34;中的数组上调用成员函数get()表示您尝试在$arr->get()上调用$arr,这是一个数组(不是对象)。您可以在CoreController的类getter中调用此类get()函数。并且该调用是由$registry属性的范围构成的,因此应该具有Object类型。

因此,在这种情况下,您应该在尝试转储protected $registry之前检查$this->router->getParams()的类型。这可能不是你所期望的那样。

我没有找到你在代码中实例化Posts类对象的地方,以及你在$registry __constructor()中放置的对象<%= form_for(@user, url: signup_path) do |f| %> <%= render 'shared/error_messages' %> <%= f.text_field :name, class: "login", placeholder: :name ...more fields... <%= f.check_box :agreement, class: "field login-checkbox" %> <label class="choice" for="Field"><%= t("agree_terms") %></label> <%= f.submit t("register"), class: "button btn btn-primary btn-large" %> <% end %> 所以我可以不检查我的猜测。如果你澄清这一点,那么找到问题会更容易。