我想问一些关于如何解决这个问题的提示。我正在尝试构建自己的 MVC 网站。我学习了 URL 的基础知识。
http://example.com/blog/cosplay/cosplayer-expo-today
博客 -> Controller cosplay -> Controller cosplayer-expo-today 中的方法 -> 方法中的变量
如果我动态扩展博客 Controller 中的类别会怎样?我是否需要创建该方法,或者是否有一些技巧可以自动执行此操作?我的意思是...我现在有这些类别:角色扮演、游戏、电影、系列。所以我需要在 Controller 中创建这些方法,但它们都做同样的事情,即从数据库中选择其他类别。
关于如何编写 Controller 以自动执行此操作有什么好的建议吗?我的意思是如果我在我的数据库中上传一个新类别,但我不想修改 Controller 。是否可以?谢谢您的帮助!
更新
这是我的 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();
}
}
}
这是我的 .htaccess
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).
自动加载和路由是分开的。
从您发布的代码来看,很明显,您有一个类,负责以下任务:
在 OOP 中有一个东西,叫做:Single Responsibility Principle [ short version ]。基本上这意味着一个类应该处理一个特定的事情。以上列表至少构成 4 您的不同责任
Autoload
类(class)。这些常规任务中的每一个都应该由一个单独的类来处理,而不是您现在拥有的。对于自动加载器,您可以使用单个功能。
如何编写自己的自动加载代码?
我看到的部分问题是关于自动加载在 PHP 中的实际工作方式的混淆。
include
的来电或 require
不需要在创建实例的地方完成。相反,您注册了一个处理程序(使用 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;
});
此特定示例使用 anonymous function ,这是 PHP 5.3 中引入的功能之一,但是
spl_autoload_register()
的手册页还将向您展示如何使用对象或普通函数实现相同的示例。另一个与自动加载密切相关的新功能是 namespaces .在这种情况下,命名空间会给您带来两个直接的好处:能够拥有多个具有相同名称的类和从多个目录加载类文件的选项。
例如,你可以有这样的代码:
$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关于如何实现自动加载器的建议。你可以找到它here .虽然它有一些重大问题,但它应该为您提供良好的基础。
如何解析漂亮的网址?
这已经不是什么 secret 了,Apache 的 mod_rewrite 在处理漂亮的 URL 方面非常有限。而且,虽然它是一个广泛使用的服务器,但它并不是网络服务器的唯一选择。这就是 PHP 开发人员选择在 PHP 端处理 URL 以获得最大灵活性的原因。
任何新手都会做的第一件事是
explode('/', ... )
.这是一个自然的选择,但您很快就会注意到它也是 极其有限它真正可以做什么。路由机制将开始增长。首先基于段数,后来 - 在段中添加不同的条件值,需要不同的行为。从本质上讲,这将变成巨大的、脆弱的和无法控制的困惑。馊主意。
相反,您应该做的是拥有一个正则表达式列表,您可以将其与给定的漂亮 URL 进行匹配。例如:
'#/(?P<resource>[^/\\\\.,;?\n]+)/foobar#'
上面定义的模式将匹配所有具有两段的 URL,第一段中有一些文本和
"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 );
活码:here或 here
Note:
If you use'(?P<name> .... )'
notation, the matches will return array with'name'
as a key. Useful trick for more then routing.
您可能希望从一些更易读的符号中生成用于匹配的正则表达式。例如,在配置文件中,这个表达式:
'#^/(?P<id>[0-9]+)(?:/(?P<resource>[^/\\\\.,;?\n]+)(?:/(?P<action>[^/\\\\.,;?\n]+))?)?$#'
.. 应该看起来像
'/:id[[/:resource]/:action]'
凡
:param
将指示一个 URL 段和 [...]
将表示 URL 的可选部分。基于此,您应该能够充实自己的路由系统。上面的代码片段只是简化核心功能的示例。要了解完全实现后的外观,您可以查看 this answer 中的代码。 .它应该会给你一些关于你自己的 API 版本的想法。
调用 Controller 上的东西..
将 Controller 的执行埋在路由类(或多个类)深处是很常见的错误。这会导致两个问题:
路由是一项任务,即使在自定义编写的应用程序中,它也会自然地倾向于代码库的“框架”部分。
(真正的)简化版本看起来像:
$matches = $router->parse( $url );
$controller = new {'\\Controller\\'.$matches['controller']};
$controller->{$matches['action']( $matches );
这样就不需要在某些类似 MVC 的架构中使用路由结果。也许您只需要一个美化的获取机制来提供静态 HTML 文件。
那些动态扩展的类别呢?
你正在以错误的方式看待它。 无需向 Controller 动态添加方法。 在您的示例中,实际上有一个 Controller 方法……类似以下内容:
public function getCategory( $request ) {
$category = $request->getParameter('category');
// ... rest of your controller method's code
}
哪里
$category
最终会包含 "cosplay"
, "game"
, "movie"
, "series"
或您添加的任何其他类别。这是您的 Controller 将传递给模型层以过滤文章的内容。人们真正专业地使用什么?
这些天来,因为每个人(好吧..每个人都有一些线索)使用 composer ,对于自动加载,最好的选择是使用 Composer 附带的加载器。
您只需添加
require __DIR__ . '/vendor/autoload.php'
有了一些配置,它就可以工作了。至于路由,有两大“独立”解决方案:FastRoute或 Symfony's Routing Component .这些可以包含在您的项目中,而不会带来额外的麻烦。
但是由于有些人将使用框架,因此每个框架也将包含路由请求的能力。
一些进一步阅读..
如果您想了解更多关于 MVC 架构模式的知识,我强烈建议您阅读 this post 中列出的所有 Material 。 .将其视为强制性阅读/观看列表。您可能还会发现我在 MVC 相关主题上的这些旧帖子有些有益:here , here和 here
P.S.: since PHP 5.0 was released (some time in 2004th), class's variables should be defined using
public
,private
orprotected
instead ofvar
.
关于php - 如何在类似 MVC 的页面中基于漂亮的 URL 加载类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18727186/