如何加载基于pretty的网址类MVC样页? [英] How to load classes based on pretty URLs in MVC-like page?

查看:594
本文介绍了如何加载基于pretty的网址类MVC样页?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想问的一些技巧,就如何解决这个问题。我试图建立自己的MVC的网站。我学到的URL的基础。

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

博客 - >控制器
角色扮演 - >在控制器中的方法
COSPLAYER,世博会今天 - >的方法变

如果我动态地扩展类在我的博客控制器?我需要创建的方法,还是有一些技巧,自动做到这一点?我的意思是......我有这几类现在:角色扮演,游戏,电影,系列。所以,我需要建立在控制这些方法,但他们都做同样的事情,即选择从数据库中其他类别。

  • 在功能角色扮演()= example.com/blog/cosplay /
  • 在功能的游戏()= example.com/blog/game /
  • 在功能的电影()= example.com/blog/movie /
  • 在功能系列()= example.com/blog/series /

有我如何能写我的控制器自动执行,任何好的建议吗?我的意思是,如果我上传一个新的类别在我的数据库,但我不希望修改控制器。可能吗?感谢您的帮助!

更新

下面是我的网址雷管类

 类自动加载
{
    变量$网址;
    变量$控制器;
    功能__construct()
    {
        $这个 - > URL = $ _GET ['URL'];
        // HNEM URES AZ网址
        如果($这个 - >!URL =''和;&安培;!空($这个 - > URL))
        {
            需要应用程序/配置/ routes.php文件;
            // URLVIZSGÁLATA
            $这个 - > rewrite_url($这个 - > URL);

            // URLSZÉTBONTÁSA
            $这个 - > URL =爆炸('/',$这个 - > URL);

            $文件='应用程序/控制器/'.$这 - >网址[0]。PHP。
            //LÉTEZIK控制器?
            如果(file_exists($文件))
            {
                需要$文件;
                $这个 - >控制器=新的$这个 - >网址[0];

                //KÉRELEMALATT VAN AZ ALOLDAL?
                如果(使用isset($这 - >网址[1]))
                {
                    //LÉTEZIK一个METÓDUS? ENGEDÉLYEZVEVAN?
                    如果(method_exists($这 - &分解控制器,$这 - >网址[1])及&安培; in_array($这 - >网址[1],$路线[$这 - >网址[0]] ))
                    {
                        如果(使用isset($这 - >网址[2]))
                        {
                            $这 - >控权> {$这 - >网址[1]}($这 - >网址[2]);
                        }
                        其他
                        {
                            $这个 - >控制器 - > {$这个 - >网址[1]}();
                        }
                    }
                    其他
                    {
                        标题(位置:SITE $这个 - >网址[0]);
                        死();
                    }
                }
            }
            其他
            {
                标题(位置:网站);
                死();
            }
        }
        其他
        {
            头('位置:'。SITE.'blog');
            死();
        }
    }

    / **
     * ELSOlépésbenmegvizsgáljuk,hogy一个kapottszövegtartalmaz-Enagybetűt。 Amennyiben IGENátalakítjukkisbetűsre< BR />
     *Másodiklépésbenmegnézzük,hogy一个kapottszöveg/ - 重新végződik-E。 Amennyiben IGENlevágjukAZT< BR />
     * HarmadiklépésbenújratöltjükAZ oldalt一个formázottszöveggel。
     *
     * @参数字符串$网址Korábbanbeolvasott URL。
     * /
    私有函数rewrite_url($网址)
    {
        // HANAGYBETŰVAN AZ URL-BEN VAGY/ -  REVÉGZŐDIK
        如果(preg_match('/ [AZ] /',$网址)|| SUBSTR($网址,-1)=='/')
        {
            //NAGYBETŰSAZ网址KICSIREALAKÍTJUK
            如果(preg_match('/ [A-Z] /',$网址))
            {
                $ URL =用strtolower($网址);
            }
            // HA'/' -  REVÉGZŐDIKLEVÁGJUK
            如果(SUBSTR($网址,-1)=='/')
            {
                $ URL = SUBSTR($网址,0,strlen的($ URL)-1);
            }
            头('位置:'SITE $网址);
            死();
        }
    }




}
 

和这里是我的.htacces

 选项+了FollowSymLinks
RewriteEngine叙述上

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

重写规则^(。+)$的index.php?URL = $ 1 [QSA,L]
 

解决方案
  

<分> 供参考:还有,你做错了几件事情。我会尝试去通过他们每个人,并说明问题,误解和可能的解决方案(S)。

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

从你的外观贴code,很明显,你有一个类,它负责以下工作:

  • 路由:它将该URL中对于应用程序的其他部分有一定意义的部分
  • 自动加载:类需要分离的网址片段,并尝试以包括与code
  • 在工厂,新的实例初始化,一些方法被称为他们
  • 响应:在某些情况下,该类发送给用户的响应中的HTTP标头的形式

在OOP有这个东西,叫做:单一职责原则 [short版本] 。基本上,它意味着一个类应该处理一个特定的事儿。上面的列表组成至少 4 作为你的自动加载类不同的责任。

而不是你现在有什么,每个这些常规任务应该由一个单独的类来处理。而如果磁带自动加载机,你可以逃脱一个单一的功能。

如何做自动加载?

这是我看到的是如何自动加载在PHP实际工作的混乱问题的部分原因。 的呼叫包括要求不需要做那里的实例将被创建。相反,你注册一个处理程序(使用<一个href="http://www.php.net/manual/en/function.spl-autoload-register.php"><$c$c>spl_autoload_register()功能),然后是** *自动调​​用,当您尝试使用previously未定义类的。

最简单的例子是:

  spl_autoload_register(函数($名称)使用($ PATH){
    $文件名= $路径。 '/'。 $名字。 .PHP;
    如果(file_exists($文件名)===真){
        需要$文件名;
        返回true;
    }
    返回false;
});
 

此特定示例使用匿名功能的,这是是PHP引入的特征之一5.3,但对于手册页 spl_autoload_register()也将显示你的例子如何使用对象或普通函数来实现相同的。

这是密切相关的自动加载另一个新功能是命名空间。在这种情况下的命名空间会给你两个直接好处:有多个类具有相同的名称和选择的能力,从多个目录装入类文件

例如,你可以有code是这样的:

  $控制器=新\控制器\概况;
$视图=新\查看\概况;

$控制器 - &GT; DoSomething的($请求);
 

。在这种情况下,你可以从 /project/controllers/overview.php 自动加载磁带机读取类和 /项目/视图/概述。 PHP 分别文件。因为 spl_autoload_register()将通过\控制器\概览\浏览\概述的处理函数。

还有一个的建议,为如何实现自动加载。你可以找到它这里。虽然它有一些显著的问题,它应该为您提供在其上建立在良好的基础。

如何解析pretty的网址?

这不是什么秘密,即Apache的的mod_rewrite 的是相当有限什么可以用的 pretty的网址的事情。而且,虽然这是一个wides $ P $垫的服务器,它不是Web服务器的唯一选择。这就是为什么最大的灵活性PHP开发人员选择来处理的URL在PHP结束。

和任何新手会做的第一件事就是爆炸('/',...)。这是一个自然的选择,但你很快就会发现,它也是极其有限在什么能真正做到。路由机制将开始成长。首先根据段数,后 - 添加的段不同的条件值,需要不同的行为

从本质上讲,这将变成巨大的,脆弱的,无法控制的混乱。糟糕的主意。

相反,你应该做的是有正规的前pressions列表,你匹配给定的pretty的URL。例如:

 #/(P&LT;资源&GT; [^ / \\\\;。吗?\ n] +)/ foobar的#
 

以上定义的模式将匹配所有具有两个事业部的网址,与和foobar的在第二第一段一些文本......像/ TESTME / foobar的

此外,你可以连接每一个模式与相应的默认值,每场比赛。当你把所有这些放在一起,你可能最终得到的配置是这样的(使用5.4+数组语法,因为的这就是我喜欢写的..对付它):

  $路线= [
    '主'=&GT; [
        '模式'=&GT; #/(P&LT;资源&GT; [^ / \\\\;。吗?\ n] +)/ foobar的#,
        默认=&GT; [
            '行动'=&GT; '标准',
        ]
    ]
    secundary'=&GT; [
        '模式'=&GT; '#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#',
        默认=&GT; [
            '资源'=&GT; '目录',
            '行动'=&GT; '视图',
        ]
    ]
    '后备'=&GT; [
        '模式'=&GT; #^ * $#',
        默认=&GT; [
            '资源'=&GT; '主要',
            '行动'=&GT; '降落',
        ]
    ]
]。
 

您可以处理使用下面的code:

  //改变这种
$ URL ='/ 12345 /产品的;

$电流=无效;

//匹配的路由
的foreach($航线为$名=&GT; $路径){
    $匹配= [];
    如果(preg_match($航线['模式'],$网址,$匹配)){
        $电流= $名称;
        $匹配= $比赛+ $航线['默认'];
        打破;
    }
}


//清理结果
的foreach(array_keys($匹配)为$键){
    如果(is_numeric($键)){
        取消设置($比赛[$关键]);
    }
}


//查看结果
后续代码var_dump($电流,$匹配);
 

<大骨节病>现场code:这里here

  

<分> 注意:
如果您使用 (?P&LT;名称&gt; ...) 符号,比赛将返回数组的 <名> 作为一个关键。有用的技巧为更多然后路由。

您可能会想一些更可读的符号生成正规前pressions的匹配。例如,在配置文件中,这前pression:

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

..或许应该看起来像

 /:ID [[/:资源] /:行动]
 

:参数会显示一个URL段和 [...] 将意味着的可选部分网址。

在此基础上,你应该能够充实自己的路由系统。上面的code片段是简化的核心功能只是例子。要获得关于如何它可能看起来全面实施一些观点,你可以看看code在这个答案。它应该给你为自己的API版本的一些想法。

的调用上的东西控制器..

这是很常见的错误埋葬控制器执行某处深在路由类(或类)。这引发了两个问题:

  • 困惑:它使更难找到这里的真正的工作,在应用程序
  • 开始
  • 联轴器:你的路由器最终链接到特定跨$ P $ MVC式的建筑风格的ptation

路由是即便在定制编写的应用程序自然会朝着codeBase包括框架十岁上下的部分吸引的任务。

的(真的)简化版本看起来像:

  $匹配= $路由器 - &GT;解析($网址);

$控制器=新{'\\控制器\\'$匹配['控制']};
$控制器 - &GT; {$匹配['行动'($匹配);
 

这样没有什么需要你的路由结果在一些MVC式的建筑风格中使用。也许你只需要提供静态的HTML文件,一个荣耀的下载控制机制。

对于那些动态地扩展类?

您正在寻找了错误的方式。 有没有需要动态地添加方法控制器在你的榜样其实是有一个控制器方法......沿着线的东西:

 公共职能的getCategory($要求){
    $类别= $请求 - &GT;的getParameter('类');

    // ...你的控制器方法的code休息
}
 

其中, $类别最终将包含角色扮演游戏电影系列或自己添加任何其他类别。它是什么,您的控制器将传递到模型层,过滤掉的文章。

部分进一步阅读。

如果您想了解更多关于MVC架构模式,我强烈建议你去,虽然都在 <强列出的材料>这个帖子 。把它看成是强制性的阅读/看清单。你也可能会发现一些好处矿上的MVC相关学科的这些老帖子:这里,的here 这里

  

<分> PS:自PHP 5.0发布(在第二千○四一些时间),类的变量应该使用定义的 公开 或受保护的,而不是私人 VAR

I would like to ask for some tips, on how solve this problem. I'm trying to build my own MVC website. I learned the basics of the URL.

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

blog -> the controller
cosplay -> the method in controller
cosplayer-expo-today ->variable in method

What if i dynamically extend the category in my blog controller? Will I need to create the method, or is there some trick to do that automatically? I mean... i have these categories now: cosplay,game,movie,series. So I need to create these methods in controller, but they all do the same thing, namely select other category from database.

  • function cosplay() = example.com/blog/cosplay/
  • function game() = example.com/blog/game/
  • function movie() = example.com/blog/movie/
  • function series() = example.com/blog/series/

Is there any good advice on how can i write my controller to do that automatically? I mean if I upload a new category in my database, but i don't want to modify the controller. Is it possible? Thanks for the help!

UPDATE

Here is my URL exploder class

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




}

And here is my .htacces

Options +FollowSymLinks
RewriteEngine On

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

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

解决方案

FYI: There are several things that you are doing wrong. I will try to go through each of them and explain the problems, the misconceptions and the possible solution(s).

The autoloading and routing are separate things.

From the look of you posted code, it's obvious, that you have a single class, which is responsible the following tasks:

  • routing: it splits the URL in parts that have some significance for the rest of applications
  • autoloading: class takes the separated URL segments and attempts to include related code
  • factory: new instances are initialized and some method are called on them
  • response: in some cases, the class sends a response to user in form of HTTP header

In OOP there is this thing, called: Single Responsibility Principle [short version]. Basically it means that a class should be handling one specific are thing. The list above constitutes at least 4 different responsibilities for your Autoload class.

Instead of what you have now, each of these general tasks should be handled by a separate class. And in case of autoloader, you could get away with a single function.

How to do autoloading ?

Part of the problem that I see is the confusion about how autoload actually works in PHP. The call of include or require doesn't need to be done where the instance will be created. Instead you register a handler (using spl_autoload_register() function), which then is **automatically* called, when you try to use previously-undefined class.

The simplest example for it is:

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

This particular example uses anonymous function, which is one of features that was introduced in PHP 5.3, but the manual page for the spl_autoload_register() will also show you examples how to achieve the same with objects or ordinary functions.

Another new feature that is closely related to autoloading is namespaces. In this context the namespaces would give you two immediate benefits: ability to have multiple classes with same name and options to load class file from multiple directories.

For example, you can have code like this:

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

$controller->doSomething( $request );

.. in this case you can have autoloader fetching classes from /project/controllers/overview.php and /project/views/overview.php files respectively. Because the spl_autoload_register() will pass "\Controllers\Overview" and "\Views\Overview" to the handler function.

There is also a FIG recommendation for how to implement autoloaders. You can find it here. While it has some significant problems, it should provide you with good base on which to build upon.

How to parse pretty URLs ?

It is no secret, that Apache's mod_rewrite is quite limited in what it can do with pretty URLs. And, while it's a widespread server, it is not the only option for webservers. This is why for maximum flexibility PHP developers opt to handle URLs on the PHP end.

And the first thing any newbie will do is explode('/', ... ). It is a natural choice, but you will soon notice that it is also extremely limited in what it can really do. The routing mechanism will start to grow. At first based on count of segments, later - adding different conditional values in segments, that require different behavior.

Essentially, this will turn in huge, fragile and uncontrollable mess. Bad idea.

Instead what you should do is have a list of regular expressions, that you match against given pretty URL. For example:

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

The above defined pattern would match all the URL that have two segments, with some text in first segment and "foobar" in the second ... like "/testme/foobar".

Additionally you can link each pattern with corresponding default values for each match. When you put this all together, you might end up with configuration like this (uses 5.4+ array syntax, because that's how I like to write .. deal with it):

$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',
        ],
    ],
]; 

Which you could handle using following code:

// 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 or here

Note:
If you use '(?P<name> .... )' notation, the matches will return array with 'name' as a key. Useful trick for more then routing.

You probably will want to generate the regular expressions for the matching from some more-readable notations. For example, in configuration file, this expression:

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

.. should probably look something like

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

Where the :param would indication an URL segment and [...] would signify an optional part of URL.

Based on this you should be able to flesh out your own routing system. The code fragments above is just example of simplified core functionality. To get some perspective on how it might look when fully implemented, you could look at code in this answer. It should give you some ideas for your own API version.

Calling the stuff on controllers ..

It is quite common mistake to bury the execution of controllers somewhere deep in the routing class (or classes).This causes two problems:

  • confusion: it makes harder to find where the "real work" begins in the application
  • coupling: your router ends up chained to that specific interpretation of MVC-like architecture

Routing is a task which even in custom-written application will naturally gravitate toward the "framework-ish" part of codebase.

The (really) simplified versions would look like:

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

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

This way there is nothing that requires your routing results to be used in some MVC-like architecture. Maybe you just need a glorified fetching mechanism for serving static HTML files.

What about those dynamically extend categories?

You are looking at it the wrong way. There is no need for dynamically adding methods to controller. In your example there is actually one controller method ... something along the lines of:

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

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

Where $category would end up containing "cosplay", "game", "movie", "series" or any other category that you have added. It is something that your controller would pass to the model layer, to filter out articles.

Some further reading ..

If you want to learn more about MVC architectural pattern, I would strongly recommend for you to go though all the materials listed in this post. Think of it as mandatory reading/watching list. You also might find somewhat beneficial these old posts of mine on the MVC related subjects: here, here and here

P.S.: since PHP 5.0 was released (some time in 2004th), class's variables should be defined using public, private or protected instead of var.

这篇关于如何加载基于pretty的网址类MVC样页?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆