小编典典

如何在类似MVC的页面中基于漂亮的URL加载类?

php

我想问一些关于如何解决这个问题的技巧。我正在尝试建立自己的MVC网站。我了解了URL的基础。

http://example.com/blog/cosplay/cosplayer-expo-today

博客->控制器 角色扮演-> 今天控制器角色扮演者博览会中的方法->方法中的变量

如果我在博客控制器中动态扩展类别会怎样?我需要创建方法,还是有一些技巧可以自动执行此操作?我的意思是…我现在有以下类别:cosplay,游戏,电影,系列。所以我需要在控制器中创建这些方法,但是它们都做相同的事情,即从数据库中选择其他类别。

关于如何编写控制器自动执行操作,是否有任何好的建议?我的意思是,如果我在数据库中上传了一个新类别,但是我不想修改控制器。可能吗?谢谢您的帮助!

更新

这是我的URL爆炸程序类

class Autoload
{
    var $url;
    var $controller;
    function __construct()
    {
        $this->url = $_GET['url'];
        //HNEM ÜRES AZ URL
        if($this->url!='' && !empty($this->url))
        {
            require 'application/config/routes.php';
            //URL VIZSGÁLATA
            $this->rewrite_url($this->url);

            //URL SZÉTBONTÁSA
            $this->url = explode('/', $this->url);

            $file = 'application/controllers/'.$this->url[0].'.php';
            //LÉTEZIK A CONTROLLER?
            if(file_exists($file))
            {
                require $file;
                $this->controller = new $this->url[0];

                //KÉRELEM ALATT VAN AZ ALOLDAL?
                if(isset($this->url[1]))
                {
                    //LÉTEZIK A METÓDUS? ENGEDÉLYEZVE VAN?
                    if(method_exists($this->controller, $this->url[1]) && in_array($this->url[1], $route[$this->url[0]]))
                    {
                        if(isset($this->url[2]))
                        {
                            $this->controller->{$this->url[1]}($this->url[2]);
                        }
                        else
                        {
                            $this->controller->{$this->url[1]}();
                        }
                    }
                    else
                    {
                        header('location:'.SITE.$this->url[0]);
                        die();
                    }
                }
            }
            else
            {
                header('location:'.SITE);
                die();
            }
        }
        else
        {
            header('location:'.SITE.'blog');
            die();
        }
    }

    /**
     * Első lépésben megvizsgáljuk, hogy a kapott szöveg tartalmaz-e nagybetűt. Amennyiben igen átalakítjuk kisbetűsre.<br/>
     * Második lépésben megnézzük, hogy a kapott szöveg '/'-re végződik-e. Amennyiben igen levágjuk azt.<br/>
     * Harmadik lépésben újra töltjük az oldalt a formázott szöveggel.
     * 
     * @param string $url Korábban beolvasott URL.
     */
    private function rewrite_url($url)
    {
        //HA NAGYBETŰ VAN AZ URL-BEN VAGY '/'-RE VÉGZŐDIK
        if(preg_match('/[A-Z]/', $url) || substr($url, -1)=='/')
        {
            //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK
            if(preg_match('/[A-Z]/', $url))
            {
                $url = strtolower($url);
            }
            //HA '/'-RE VÉGZŐDIK LEVÁGJUK
            if(substr($url, -1)=='/')
            {
                $url = substr($url, 0, strlen($url)-1);
            }
            header('location:'.SITE.$url);
            die();
        }
    }




}

这是我的.htacces

Options +FollowSymLinks
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d  
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]

阅读 333

收藏
2020-05-26

共1个答案

小编典典

仅供参考: 您做错了几件事。我将尝试遍历每个问题,并解释问题,误解和可能的解决方案。

自动加载和路由是分开的。

从您发布的代码的外观来看,很明显,您只有一个类,它负责以下任务:

  • 路由:将URL分成对其余应用程序有重要意义的部分
  • 自动加载:类采用分隔的URL段并尝试包含相关代码
  • factory:初始化新实例,并在其上调用某些方法
  • 响应:在某些情况下,该类以HTTP标头的形式向用户发送响应

在OOP中有一个东西,叫做:单一责任原则
。基本上,这意味着一个类应该处理一个特定的事物。上面的列表构成了您班上至少 4 种不同的职责Autoload

这些常规任务中的每一个都应该由一个单独的类处理,而不是现在拥有的。如果使用自动装带器,则可以使用一个功能。

如何编写自己的自动加载代码?

我看到的部分问题是对自动加载在PHP中的实际工作方式感到困惑。在要创建实例的位置进行的调用includerequire不需要进行调用。相反spl_autoload_register(),您尝试注册处理程序(使用函数),然后在尝试使用以前未定义的类时被“自动”调用。

最简单的例子是:

spl_autoload_register( function( $name ) use ( $path ) {
    $filename = $path . '/' . $name . '.php';
    if ( file_exists( $filename ) === true ) {
        require $filename;
        return true;
    }
    return false;
});

这个特定的示例使用匿名函数,这是PHP5.3中引入的功能之一,但是的手册页spl_autoload_register()还将向您展示示例如何使用对象或普通函数实现相同的功能。

与自动加载密切相关的另一个新功能是名称空间。在这种情况下,名称空间将为您带来两个直接的好处:具有多个具有相同名称的类的能力以及从多个目录中加载类文件的选项。

例如,您可以具有以下代码:

$controller = new \Controllers\Overview;
$view = new \Views\Overview;

$controller->doSomething( $request );

..在这种情况下,您可以让自动加载器分别从/project/controllers/overview.php/project/views/overview.php文件中提取类。因为spl_autoload_register()将传递"\Controllers\Overview"并传递"\Views\Overview"给处理程序函数。

还有一个关于如何实现自动加载器的FIG建议。你可以在这里找到它。尽管存在一些重大问题,但它应该为您提供良好的基础。

如何解析漂亮的URL?

毫无疑问,Apache的 mod_rewrite 在使用 漂亮的URL方面的功能
受到很大限制。而且,尽管它是一种广泛使用的服务器,但它并不是Web服务器的唯一选择。这就是为什么为了获得最大的灵活性,PHP开发人员选择在PHP端处理URL。

而且,任何新手都会做的第一件事是explode('/', ... )。这是很自然的选择,但是您很快就会注意到,它的实际功能也 极为有限
。路由机制将开始发展。首先,基于段的数量,然后-在段中添加需要不同行为的不同条件值。

本质上,这将导致巨大,脆弱和不可控制的混乱。馊主意。

相反,您应该做的是拥有一个正则表达式列表,将它们与给定的漂亮URL匹配。例如:

'#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#'

上述定义的模式将匹配所有具有两段,在第一段一些文字和网址"foobar"在第二…像"/testme/foobar"

另外,您可以将每个模式与每个匹配项的相应默认值链接起来。当将所有这些放在一起时,您可能最终会得到如下配置(使用5.4+数组语法,因为 这就是我喜欢编写
..处理它的方式):

$routes = [
    'primary' => [
        'pattern'   => '#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#',
        'default'   => [
            'action'    => 'standard',
        ],
    ],
    'secundary' => [
        'pattern'   => '#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#',
        'default'   => [
            'resource'  => 'catalog',
            'action'    => 'view',
        ]
    ],
    'fallback'  => [
        'pattern'   => '#^.*$#',
        'default'   => [
            'resource'  => 'main',
            'action'    => 'landing',
        ],
    ],
];

您可以使用以下代码处理:

// CHANGE THIS
$url = '/12345/product';

$current = null;

// matching the route
foreach ($routes as $name => $route) {
    $matches = [];
    if ( preg_match( $route['pattern'], $url, $matches ) ) {
        $current = $name;
        $matches = $matches + $route['default'];
        break;
    }
}


// cleaning up results
foreach ( array_keys($matches) as $key ) {
    if ( is_numeric($key) ) {
        unset( $matches[$key] );
    }
}


// view results
var_dump( $current, $matches );

Live code: [here](http://3v4l.org/T7i3l) or [here](http://codepad.viper-7.com/VOCWZh)

注意:
如果使用 '(?P<name> .... )'符号,则匹配项将以 'name'
键作为键返回数组。有用的技巧,然后进行更多路由。

您可能会希望从一些可读性更高的符号生成匹配的正则表达式。例如,在配置文件中,此表达式:

'#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#'

..应该看起来像

'/:id[[/:resource]/:action]'

其中:param表示URL段,[...]并表示URL的可选部分。

基于此,您应该能够充实自己的路由系统。上面的代码片段只是简化的核心功能的示例。要对完全实现后的外观有所了解。它应该给您一些有关您自己的API版本的想法。

调用控制器上的东西..

将控制器的执行掩埋在一个或多个路由类中的某个位置很常见,这会导致两个问题:

  • 混乱:很难找到应用程序中“实际工作”的开始位置
  • 耦合:您的路由器最终链接到对MVC架构的特定解释

路由是一项任务,即使在自定义编写的应用程序中,路由也自然会吸引到代码库的“框架化”部分。

(真正的)简化版本如下所示:

$matches = $router->parse( $url );

$controller = new {'\\Controller\\'.$matches['controller']};
$controller->{$matches['action']( $matches );

这样,没有什么要求您的路由结果在某些类似MVC的体系结构中使用。也许您只需要一种出色的获取机制来提供静态HTML文件即可。

那那些动态扩展类别呢?

您正在以错误的方式看待它。 无需向控制器动态添加方法。 在您的示例中,实际上有一个控制器方法…类似以下内容:

public function getCategory( $request ) {
    $category = $request->getParameter('category');

    // ... rest of your controller method's code
}

$category最终将含有"cosplay""game""movie""series"或任何其他类别已添加。您的控制器会将此信息传递给模型层,以过滤掉文章。

人们实际上在专业上使用什么?

这些天来,由于每个人(好吧,每个都有线索的人)都使用composer,自动加载的最佳选择是使用composer随附的加载程序。

您只需添加,require __DIR__ . '/vendor/autoload.php'并进行一些配置即可使用。

至于路由,有两种主要的“独立”解决方案:FastRouteSymfony的Routing
Component
。这些可以包含在您的项目中,而不会造成其他麻烦。

但是由于某些人将使用框架,因此每个框架还将包含路由请求的功能。

2020-05-26