如何在类似MVC的页面中基于漂亮的URL加载类?
我想问一些提示,关于如何解决这个问题。 我试图build立自己的MVC网站。 我了解了URL的基础知识。
http://example.com/blog/cosplay/cosplayer-expo-today
博客 – >控制器
angular色扮演 – >控制器中的方法
cosplayer-expo-today – >方法中的variables
如果我在我的博客控制器中dynamic扩展类别,该怎么办? 我是否需要创build方法,或者是否有一些技巧会自动执行? 我的意思是…我现在有这些类别:angular色扮演,游戏,电影,系列。 所以我需要在控制器中创build这些方法,但他们都做同样的事情,即从数据库中select其他类别。
- functionangular色扮演()= example.com/blog/cosplay/
- 函数游戏()= example.com/blog/game/
- 函数movie()= example.com/blog/movie/
- 函数系列()= example.com/blog/series/
有没有什么好的build议,我怎么能写我的控制器来做到这一点? 我的意思是如果我上传一个新的类别在我的数据库,但我不想修改控制器。 可能吗? 谢谢您的帮助!
UPDATE
这是我的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('/[AZ]/', $url) || substr($url, -1)=='/') { //NAGYBETŰS AZ URL KICSIRE ALAKÍTJUK if(preg_match('/[AZ]/', $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]
仅供参考:有几件事你做错了。 我会尽力去解释每个问题,解释问题,误解和可能的解决方法。
自动加载和路由是分开的事情。
从你发布的代码看,很明显,你有一个类,它负责以下任务:
- 路由:它将url分成多个部分,对其他应用程序有一些重要意义
- 自动加载:类采取分离的网段,并尝试包括相关的代码
- 工厂:新的实例被初始化,有些方法被调用
- 响应:在某些情况下,类以HTTP头的forms向用户发送响应
在OOP中有这样的东西,叫做: Single Responsibility Principle [ 简短版本 ] 。 基本上这意味着一个class级应该处理一个具体的事情。 上面的列表对您的Autoload
类至less构成4个不同的责任。
这些普通任务中的每一个都应该由一个单独的类来处理,而不是你现在拥有的。 而在自动加载器的情况下,你可以逃脱一个单一的function。
如何自动加载?
我看到的部分问题是关于autoload在PHP中如何工作的混淆。 include
或require
的调用require
在创build实例的地方完成。 相反,您注册一个处理程序(使用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; });
这个特殊的例子使用匿名函数 ,这是PHP 5.3中引入的function之一,但是spl_autoload_register()
的手册页也将向您展示如何实现与对象或普通函数相同的示例。
与自动加载密切相关的另一个新function是名称空间 。 在这种情况下,命名空间会给你两个直接的好处:能够拥有多个具有相同名称的类和从多个目录加载类文件的选项。
例如,你可以有这样的代码:
$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"
传递给处理函数。
还有一个关于如何实现自动加载器的FIGbuild议。 你可以在这里find它。 虽然它有一些重大的问题,它应该为您提供良好的基础上build立。
如何parsing漂亮的url?
Apache的mod_rewrite在使用漂亮的URL的时候是相当有限的,这并不是什么秘密。 而且,虽然这是一个广泛的服务器,但它不是networking服务器的唯一select。 这就是为什么最大的灵活性PHP开发人员select处理PHP端的URL。
而新手会做的第一件事是explode('/', ... )
。 这是一个很自然的select,但是你很快就会注意到它的真正作用也是非常有限的 。 路由机制将开始增长。 首先基于段的数量,稍后 – 在段中添加不同的条件值,这要求不同的行为。
本质上,这会变成巨大的,脆弱的,无法控制的混乱。 馊主意。
相反,你应该做的是有一个正则expression式的列表,你匹配给定的漂亮的URL。 例如:
'#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#'
上面定义的模式可以匹配所有有两段的URL,第一段有一些文本,第二段有"foobar"
,比如"/testme/foobar"
。
此外,您可以将每个模式与每个比赛的相应默认值相关联。 当你把这一切放在一起,你可能最终像这样的configuration(使用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 );
现场代码: 在这里或这里
注意:
如果使用'(?P<name> .... )'
符号,则匹配将以'name'
作为键返回数组。 有用的技巧更多然后路由。
你可能会想从一些更易读的符号生成匹配的正则expression式。 例如,在configuration文件中,这个expression式:
'#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#'
..应该可能看起来像
'/:id[[/:resource]/:action]'
其中:param
将指示一个URL段,并表示URL的一个可选部分。
基于此,您应该能够充实自己的路由系统。 上面的代码片段只是简化核心function的例子。 要充分实现它的外观,你可以看看这个答案中的代码。 它应该给你一些你自己的API版本的想法。
调用控制器上的东西..
将控制器的执行埋在路由类(或类)的深处是非常常见的错误。这会导致两个问题:
- 混淆:在应用程序中很难find“实际工作”开始的地方
- 耦合:您的路由器最终链接到MVC类架构的特定解释
路由是一项任务,即使在自定义编写的应用程序中,也会自然而然地倾向于代码库的“框架”部分。
(真的)简化版本看起来像这样:
$matches = $router->parse( $url ); $controller = new {'\\Controller\\'.$matches['controller']}; $controller->{$matches['action']( $matches );
这样就没有什么需要你的路由结果在一些类似MVC的体系结构中使用。 也许你只需要一个提供静态HTML文件的光荣的获取机制。
那些dynamic扩展类别呢?
你正在看错误的方式。 没有必要为控制器dynamic添加方法。 在你的例子中,实际上有一个控制器方法…沿着以下几点:
public function getCategory( $request ) { $category = $request->getParameter('category'); // ... rest of your controller method's code }
其中$category
将包含"cosplay"
, "game"
, "movie"
, "series"
或您添加的任何其他类别。 这是你的控制器将传递到模型层,过滤掉文章的东西。
进一步阅读..
如果您想了解更多关于MVC架构模式的信息,我强烈build议您阅读本文中列出的所有资料。 把它看成是强制性的阅读/看清单。 在MVC相关主题上,您也可能会发现这些旧post对我有些帮助: 这里 , 这里和这里
PS:自PHP 5.0发布以来(2004年的一段时间),类的variables应该使用
public
,private
或protected
来定义,而不是var
。
我想你是在想这个…
所以控制器/资源是一个博客….所有他们应该运行的方法是“读”(使用crud …我通常把它称为数据,但基本上它是你的select)。 现在只要让你的方法接受一个类别值,自动根据url映射….
这里是一个样本…完全未经testing,但只是为了告诉你这个想法(使用pdo)
public function data($id = 0, $category = 0){ if (isset($id) AND $id != 0){ $bind = array(":id", $id); $results = $db->query("SELECT * FROM blog WHERE blog_id = :id", $bind); return $results[0]; } else if (isset($category) AND $id != 0){ $approved_categories = array("cosplay","game","movie","series"); if (in_array($category, $approved_categories)){ $bind = array(":cat", $category); $results = $db->query("SELECT * FROM blog WHERE blog_cat = :cat", $bind); } return $results; } }